Compare commits
2 Commits
step35/147
...
step35/162
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
180464cc5e | ||
|
|
4b5a675355 |
@@ -1,540 +0,0 @@
|
||||
|
||||
---
|
||||
domain: global
|
||||
category: pattern
|
||||
version: 1
|
||||
last_updated: '2026-04-26T11:22:05.449323+00:00'
|
||||
facts:
|
||||
- id: cross-repo:connection:001
|
||||
fact: "hermes-agent references compounding-intelligence via issue #229: [ATLAS]\
|
||||
\ Steal the highest-leverage ecosystem patterns into the sovereign stack \u2192\
|
||||
\ [compounding-intelligence#229] [ATLAS][Telemetry] Build local token and cost\
|
||||
\ ingestion using Tokscale core patterns"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:56.905616+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:56.905641+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:002
|
||||
fact: "hermes-agent references compounding-intelligence via issue #231: [ATLAS]\
|
||||
\ Steal the highest-leverage ecosystem patterns into the sovereign stack \u2192\
|
||||
\ [compounding-intelligence#231] [ATLAS][Wiki] Build the LLM Wiki layer: ingest,\
|
||||
\ query, lint, and session crystallization into durabl"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:57.230615+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:57.230632+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:003
|
||||
fact: "hermes-agent references compounding-intelligence via issue #232: [ATLAS]\
|
||||
\ Steal the highest-leverage ecosystem patterns into the sovereign stack \u2192\
|
||||
\ [compounding-intelligence#232] [ATLAS][Research] Solve the swarm-memory gap\
|
||||
\ for concurrent subagents"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:57.415552+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:57.415580+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:004
|
||||
fact: "hermes-agent references fleet-ops via issue #410: [ATLAS] Steal the highest-leverage\
|
||||
\ ecosystem patterns into the sovereign stack \u2192 [fleet-ops#410] [ATLAS][Safety]\
|
||||
\ Deploy a virtual-credential and DLP sidecar for sovereign agent runs"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:57.716479+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:57.716488+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:005
|
||||
fact: "hermes-agent references the-nexus via issue #893: [CONSOLIDATED] Fleet SITREP\
|
||||
\ \u2014 Allegro 7-Task Execution + Ezra Deliverables \u2192 [the-nexus#893] [AUDIT]\
|
||||
\ System Formalization \u2014 OSS Replacements & Cleanup Candidates"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:57.961498+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:57.961510+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:006
|
||||
fact: "hermes-agent references the-nexus via issue #1695: [ATLAS] Steal the highest-leverage\
|
||||
\ ecosystem patterns into the sovereign stack \u2192 [the-nexus#1695] [ATLAS][Cockpit]\
|
||||
\ Build the operator inspector rail and session shell patterns worth stealing"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:58.130733+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:58.130745+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:007
|
||||
fact: "hermes-agent references timmy-config via issue #875: [ATLAS] Steal the highest-leverage\
|
||||
\ ecosystem patterns into the sovereign stack \u2192 [timmy-config#875] [ATLAS][Skills]\
|
||||
\ Import Karpathy-style cognitive principles and GStack-style role commands as\
|
||||
\ native "
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:58.327960+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:58.327972+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:008
|
||||
fact: "hermes-agent references timmy-dispatch via issue #18: [ATLAS] Steal the highest-leverage\
|
||||
\ ecosystem patterns into the sovereign stack \u2192 [timmy-dispatch#18] [ATLAS][Dispatch]\
|
||||
\ Add Mission Control-style dispatch ledger, capacity model, and heartbeat tracking"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:58.502966+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:58.502981+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:009
|
||||
fact: "hermes-agent references timmy-home via issue #568: [DISPATCH] Ezra \u2014\
|
||||
\ Investigate & Install MemPalace on Hermes VPS \u2192 [timmy-home#568] [EVALUATION]\
|
||||
\ MemPalace v3.0.0 Integration \u2014 Before/After Metrics + Recommendation"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:58.967894+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:58.967906+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:010
|
||||
fact: "the-nexus references hermes-agent via issue #984: [ATLAS][Cockpit] Build\
|
||||
\ the operator inspector rail and session shell patterns worth stealing \u2192\
|
||||
\ [hermes-agent#984] [ATLAS] Steal the highest-leverage ecosystem patterns into\
|
||||
\ the sovereign stack"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:59.445454+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:59.445470+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:011
|
||||
fact: "timmy-config references the-nexus via issue #501: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-26 (Thursday) \u2192 [the-nexus#501] [claude] Re-implement\
|
||||
\ gravity anomaly zones (#478)"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:21:59.975253+00:00'
|
||||
last_confirmed: '2026-04-26T11:21:59.975291+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:012
|
||||
fact: "timmy-config references the-nexus via issue #502: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-26 (Thursday) \u2192 [the-nexus#502] [claude] Time-lapse\
|
||||
\ replay mode (#484)"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:00.193542+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:00.193552+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:013
|
||||
fact: "timmy-config references the-nexus via issue #503: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-26 (Thursday) \u2192 [the-nexus#503] [claude] InstancedMesh\
|
||||
\ optimizations for repeated geometry (#482)"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:00.515208+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:00.515229+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:014
|
||||
fact: "timmy-config references the-nexus via issue #552: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-26 (Thursday) \u2192 [the-nexus#552] [HEARTBEAT] Apprentice\
|
||||
\ protocol \u2014 episodic memory logging of desktop activity"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:00.658814+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:00.658829+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:015
|
||||
fact: "timmy-config references the-nexus via issue #554: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-26 (Thursday) \u2192 [the-nexus#554] [ECONOMY] Stand up\
|
||||
\ Cashu Mint on Lightning"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:01.089278+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:01.089287+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:016
|
||||
fact: "timmy-config references the-nexus via issue #555: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-26 (Thursday) \u2192 [the-nexus#555] [ECONOMY] Prototype\
|
||||
\ Bannerlord Denar \u2192 Cashu token Harmony patch"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:01.297062+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:01.297069+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:017
|
||||
fact: "timmy-config references the-nexus via issue #613: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-28 (Saturday) \u2192 [the-nexus#613] [gemini] [HARNESS]\
|
||||
\ Shadow Context Manager \u2014 auto-generated brain dump for session continuity\
|
||||
\ (#608)"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:01.814895+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:01.814905+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:018
|
||||
fact: "timmy-config references the-nexus via issue #673: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-28 (Saturday) \u2192 [the-nexus#673] [PORTAL] Deterministic\
|
||||
\ Morrowind pilot loop with world-state proof"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:02.118691+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:02.118711+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:019
|
||||
fact: "timmy-config references the-nexus via issue #674: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-28 (Saturday) \u2192 [the-nexus#674] [PORTAL] Reflex tactical\
|
||||
\ layer and semantic trajectory logging"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:02.261655+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:02.261669+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:020
|
||||
fact: "timmy-config references the-nexus via issue #675: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-28 (Saturday) \u2192 [the-nexus#675] [HARNESS] Deterministic\
|
||||
\ context compaction for long local sessions"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:02.403879+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:02.403922+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:021
|
||||
fact: "timmy-config references the-nexus via issue #676: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-28 (Saturday) \u2192 [the-nexus#676] [gemini] [PORTAL]\
|
||||
\ Reflex tactical layer and semantic trajectory logging (#674)"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:02.545921+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:02.545931+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:022
|
||||
fact: "timmy-config references the-nexus via issue #677: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-28 (Saturday) \u2192 [the-nexus#677] [gemini] [HARNESS]\
|
||||
\ Deterministic context compaction for long local sessions (#675)"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:02.691982+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:02.691988+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:023
|
||||
fact: "timmy-config references the-nexus via issue #730: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-29 (Sunday) \u2192 [the-nexus#730] [VISUAL] Give Workshop,\
|
||||
\ Archive, Chapel, Courtyard, and Gate distinct Nexus visual identities"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:03.030374+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:03.030404+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:024
|
||||
fact: "timmy-config references the-nexus via issue #731: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-29 (Sunday) \u2192 [the-nexus#731] [VALIDATION] Browser\
|
||||
\ smoke + visual proof for the Evennia-fed Nexus shell"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:03.245877+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:03.245885+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:025
|
||||
fact: "timmy-config references the-nexus via issue #736: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-29 (Sunday) \u2192 [the-nexus#736] Perplexity review"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:03.471514+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:03.471536+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:026
|
||||
fact: "timmy-config references the-nexus via issue #785: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-30 (Monday) \u2192 [the-nexus#785] Implement SharedArrayBuffer\
|
||||
\ for Zero-Copy Data Sharing"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:03.598910+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:03.598925+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:027
|
||||
fact: "timmy-config references the-nexus via issue #786: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-30 (Monday) \u2192 [the-nexus#786] Implement GPU-Accelerated\
|
||||
\ Knowledge Graph Queries"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:03.762768+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:03.762779+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:028
|
||||
fact: "timmy-config references the-nexus via issue #787: \u2600\uFE0F Good Morning\
|
||||
\ Report \u2014 2026-03-30 (Monday) \u2192 [the-nexus#787] Implement Hierarchical\
|
||||
\ L1/L2 Cache for Meta-Reasoning"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:03.917083+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:03.917092+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:029
|
||||
fact: "timmy-home references hermes-agent via issue #72: \U0001F525 Burn Report\
|
||||
\ #5 \u2014 2026-04-01 \u2014 Crisis Safety Test Infrastructure \u2192 [hermes-agent#72]\
|
||||
\ [RED TEAM] Full Jailbreak Audit - Claude Opus 4.6 - 2026-03-30"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:04.071933+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:04.071949+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:030
|
||||
fact: "timmy-home references hermes-agent via issue #74: [BURN REPORT] SHIELD Security\
|
||||
\ Implementation - Issues #72/#74/#75 \u2192 [hermes-agent#74] [RED TEAM] ULTRAPLINIAN\
|
||||
\ Crisis Stress Test \u2014 22 CRITICAL Failures \u2014 2026-03-31"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:04.214487+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:04.214499+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:031
|
||||
fact: "timmy-home references hermes-agent via issue #75: [BURN REPORT] SHIELD Security\
|
||||
\ Implementation - Issues #72/#74/#75 \u2192 [hermes-agent#75] [SHIELD] Jailbreak\
|
||||
\ Input Detection Module \u2014 Built Overnight From Red Team Findings"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:04.369176+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:04.369184+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:032
|
||||
fact: "timmy-home references hermes-agent via issue #79: \U0001F525 Burn Report\
|
||||
\ #5 \u2014 2026-04-01 \u2014 Crisis Safety Test Infrastructure \u2192 [hermes-agent#79]\
|
||||
\ [SECURITY] [CRITICAL] Test og_godmode template against crisis/suicide safety\
|
||||
\ queries"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:04.884029+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:04.884062+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:033
|
||||
fact: "timmy-home references hermes-agent via issue #80: \U0001F525 Burn Report\
|
||||
\ #5 \u2014 2026-04-01 \u2014 Crisis Safety Test Infrastructure \u2192 [hermes-agent#80]\
|
||||
\ [SECURITY] [HIGH] Implement input sanitization for GODMODE jailbreak patterns"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:05.116347+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:05.116357+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:034
|
||||
fact: "timmy-home references hermes-agent via issue #81: \U0001F525 Burn Report\
|
||||
\ #5 \u2014 2026-04-01 \u2014 Crisis Safety Test Infrastructure \u2192 [hermes-agent#81]\
|
||||
\ [SECURITY] [MEDIUM] ULTRAPLINIAN follow-up: Red team fallback chain (Kimi, Gemini,\
|
||||
\ Grok, Llama)"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:05.279272+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:05.279284+00:00'
|
||||
related: []
|
||||
- id: cross-repo:connection:035
|
||||
fact: "timmy-home references the-nexus via issue #859: [AGENDA] Next burn night\
|
||||
\ \u2014 amplify bounded quick wins, proof-first comments, and overnight leverage\
|
||||
\ \u2192 [the-nexus#859] [AUTOGENESIS][Phase I] Hermes v2.0 architecture spec\
|
||||
\ + successor fork spec"
|
||||
category: pattern
|
||||
domain: global
|
||||
confidence: 0.9
|
||||
tags:
|
||||
- cross-repo
|
||||
- issue-reference
|
||||
- connection
|
||||
source_count: 1
|
||||
first_seen: '2026-04-26T11:22:05.448200+00:00'
|
||||
last_confirmed: '2026-04-26T11:22:05.448207+00:00'
|
||||
related: []
|
||||
1060
knowledge/index.json
1060
knowledge/index.json
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
||||
# Cross-Repo Connection Report
|
||||
|
||||
Generated: 2026-04-26T11:22:05.466516+00:00
|
||||
Org: Timmy_Foundation
|
||||
Repos scanned: 21
|
||||
|
||||
## Connections
|
||||
|
||||
### hermes-agent
|
||||
|
||||
- hermes-agent references compounding-intelligence via issue #229: [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack → [compounding-intelligence#229] [ATLAS][Telemetry] Build local token and cost ingestion using Tokscale core patterns
|
||||
- hermes-agent references compounding-intelligence via issue #231: [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack → [compounding-intelligence#231] [ATLAS][Wiki] Build the LLM Wiki layer: ingest, query, lint, and session crystallization into durabl
|
||||
- hermes-agent references compounding-intelligence via issue #232: [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack → [compounding-intelligence#232] [ATLAS][Research] Solve the swarm-memory gap for concurrent subagents
|
||||
- hermes-agent references fleet-ops via issue #410: [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack → [fleet-ops#410] [ATLAS][Safety] Deploy a virtual-credential and DLP sidecar for sovereign agent runs
|
||||
- hermes-agent references the-nexus via issue #893: [CONSOLIDATED] Fleet SITREP — Allegro 7-Task Execution + Ezra Deliverables → [the-nexus#893] [AUDIT] System Formalization — OSS Replacements & Cleanup Candidates
|
||||
- hermes-agent references the-nexus via issue #1695: [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack → [the-nexus#1695] [ATLAS][Cockpit] Build the operator inspector rail and session shell patterns worth stealing
|
||||
- hermes-agent references timmy-config via issue #875: [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack → [timmy-config#875] [ATLAS][Skills] Import Karpathy-style cognitive principles and GStack-style role commands as native
|
||||
- hermes-agent references timmy-dispatch via issue #18: [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack → [timmy-dispatch#18] [ATLAS][Dispatch] Add Mission Control-style dispatch ledger, capacity model, and heartbeat tracking
|
||||
- hermes-agent references timmy-home via issue #568: [DISPATCH] Ezra — Investigate & Install MemPalace on Hermes VPS → [timmy-home#568] [EVALUATION] MemPalace v3.0.0 Integration — Before/After Metrics + Recommendation
|
||||
|
||||
### the-nexus
|
||||
|
||||
- the-nexus references hermes-agent via issue #984: [ATLAS][Cockpit] Build the operator inspector rail and session shell patterns worth stealing → [hermes-agent#984] [ATLAS] Steal the highest-leverage ecosystem patterns into the sovereign stack
|
||||
|
||||
### timmy-config
|
||||
|
||||
- timmy-config references the-nexus via issue #501: ☀️ Good Morning Report — 2026-03-26 (Thursday) → [the-nexus#501] [claude] Re-implement gravity anomaly zones (#478)
|
||||
- timmy-config references the-nexus via issue #502: ☀️ Good Morning Report — 2026-03-26 (Thursday) → [the-nexus#502] [claude] Time-lapse replay mode (#484)
|
||||
- timmy-config references the-nexus via issue #503: ☀️ Good Morning Report — 2026-03-26 (Thursday) → [the-nexus#503] [claude] InstancedMesh optimizations for repeated geometry (#482)
|
||||
- timmy-config references the-nexus via issue #552: ☀️ Good Morning Report — 2026-03-26 (Thursday) → [the-nexus#552] [HEARTBEAT] Apprentice protocol — episodic memory logging of desktop activity
|
||||
- timmy-config references the-nexus via issue #554: ☀️ Good Morning Report — 2026-03-26 (Thursday) → [the-nexus#554] [ECONOMY] Stand up Cashu Mint on Lightning
|
||||
- timmy-config references the-nexus via issue #555: ☀️ Good Morning Report — 2026-03-26 (Thursday) → [the-nexus#555] [ECONOMY] Prototype Bannerlord Denar → Cashu token Harmony patch
|
||||
- timmy-config references the-nexus via issue #613: ☀️ Good Morning Report — 2026-03-28 (Saturday) → [the-nexus#613] [gemini] [HARNESS] Shadow Context Manager — auto-generated brain dump for session continuity (#608)
|
||||
- timmy-config references the-nexus via issue #673: ☀️ Good Morning Report — 2026-03-28 (Saturday) → [the-nexus#673] [PORTAL] Deterministic Morrowind pilot loop with world-state proof
|
||||
- timmy-config references the-nexus via issue #674: ☀️ Good Morning Report — 2026-03-28 (Saturday) → [the-nexus#674] [PORTAL] Reflex tactical layer and semantic trajectory logging
|
||||
- timmy-config references the-nexus via issue #675: ☀️ Good Morning Report — 2026-03-28 (Saturday) → [the-nexus#675] [HARNESS] Deterministic context compaction for long local sessions
|
||||
- timmy-config references the-nexus via issue #676: ☀️ Good Morning Report — 2026-03-28 (Saturday) → [the-nexus#676] [gemini] [PORTAL] Reflex tactical layer and semantic trajectory logging (#674)
|
||||
- timmy-config references the-nexus via issue #677: ☀️ Good Morning Report — 2026-03-28 (Saturday) → [the-nexus#677] [gemini] [HARNESS] Deterministic context compaction for long local sessions (#675)
|
||||
- timmy-config references the-nexus via issue #730: ☀️ Good Morning Report — 2026-03-29 (Sunday) → [the-nexus#730] [VISUAL] Give Workshop, Archive, Chapel, Courtyard, and Gate distinct Nexus visual identities
|
||||
- timmy-config references the-nexus via issue #731: ☀️ Good Morning Report — 2026-03-29 (Sunday) → [the-nexus#731] [VALIDATION] Browser smoke + visual proof for the Evennia-fed Nexus shell
|
||||
- timmy-config references the-nexus via issue #736: ☀️ Good Morning Report — 2026-03-29 (Sunday) → [the-nexus#736] Perplexity review
|
||||
- timmy-config references the-nexus via issue #785: ☀️ Good Morning Report — 2026-03-30 (Monday) → [the-nexus#785] Implement SharedArrayBuffer for Zero-Copy Data Sharing
|
||||
- timmy-config references the-nexus via issue #786: ☀️ Good Morning Report — 2026-03-30 (Monday) → [the-nexus#786] Implement GPU-Accelerated Knowledge Graph Queries
|
||||
- timmy-config references the-nexus via issue #787: ☀️ Good Morning Report — 2026-03-30 (Monday) → [the-nexus#787] Implement Hierarchical L1/L2 Cache for Meta-Reasoning
|
||||
|
||||
### timmy-home
|
||||
|
||||
- timmy-home references hermes-agent via issue #72: 🔥 Burn Report #5 — 2026-04-01 — Crisis Safety Test Infrastructure → [hermes-agent#72] [RED TEAM] Full Jailbreak Audit - Claude Opus 4.6 - 2026-03-30
|
||||
- timmy-home references hermes-agent via issue #74: [BURN REPORT] SHIELD Security Implementation - Issues #72/#74/#75 → [hermes-agent#74] [RED TEAM] ULTRAPLINIAN Crisis Stress Test — 22 CRITICAL Failures — 2026-03-31
|
||||
- timmy-home references hermes-agent via issue #75: [BURN REPORT] SHIELD Security Implementation - Issues #72/#74/#75 → [hermes-agent#75] [SHIELD] Jailbreak Input Detection Module — Built Overnight From Red Team Findings
|
||||
- timmy-home references hermes-agent via issue #79: 🔥 Burn Report #5 — 2026-04-01 — Crisis Safety Test Infrastructure → [hermes-agent#79] [SECURITY] [CRITICAL] Test og_godmode template against crisis/suicide safety queries
|
||||
- timmy-home references hermes-agent via issue #80: 🔥 Burn Report #5 — 2026-04-01 — Crisis Safety Test Infrastructure → [hermes-agent#80] [SECURITY] [HIGH] Implement input sanitization for GODMODE jailbreak patterns
|
||||
- timmy-home references hermes-agent via issue #81: 🔥 Burn Report #5 — 2026-04-01 — Crisis Safety Test Infrastructure → [hermes-agent#81] [SECURITY] [MEDIUM] ULTRAPLINIAN follow-up: Red team fallback chain (Kimi, Gemini, Grok, Llama)
|
||||
- timmy-home references the-nexus via issue #859: [AGENDA] Next burn night — amplify bounded quick wins, proof-first comments, and overnight leverage → [the-nexus#859] [AUTOGENESIS][Phase I] Hermes v2.0 architecture spec + successor fork spec
|
||||
|
||||
|
||||
Total connections: 35
|
||||
366
scripts/code_duplication_detector.py
Normal file
366
scripts/code_duplication_detector.py
Normal file
@@ -0,0 +1,366 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Code Duplication Detector — Issue #162
|
||||
|
||||
Finds duplicate functions and code blocks across Python source files.
|
||||
Reports duplication percentage and outputs a duplication report.
|
||||
|
||||
Usage:
|
||||
python3 scripts/code_duplication_detector.py --output reports/code_duplication.json
|
||||
python3 scripts/code_duplication_detector.py --directory scripts/ --dry-run
|
||||
python3 scripts/code_duplication_detector.py --test # Run built-in test
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Tuple, Optional
|
||||
|
||||
|
||||
# ── AST helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
def normalize_code(text: str) -> str:
|
||||
"""Normalize code for comparison: strip comments, normalize whitespace."""
|
||||
# Remove comments (both # and docstring triple-quote strings)
|
||||
text = re.sub(r'#.*$', '', text, flags=re.MULTILINE)
|
||||
text = re.sub(r'""".*?"""', '', text, flags=re.DOTALL)
|
||||
text = re.sub(r"'''.*?'''", '', text, flags=re.DOTALL)
|
||||
# Normalize whitespace
|
||||
text = re.sub(r'\s+', ' ', text).strip()
|
||||
return text.lower()
|
||||
|
||||
|
||||
def code_hash(text: str) -> str:
|
||||
"""SHA256 hash of normalized code for exact duplicate detection."""
|
||||
normalized = normalize_code(text)
|
||||
return hashlib.sha256(normalized.encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
# ── Function extraction via AST ────────────────────────────────────────────
|
||||
|
||||
class FunctionExtractor:
|
||||
"""Extract function and method definitions with their full source bodies."""
|
||||
|
||||
def __init__(self, source: str, filepath: str):
|
||||
self.source = source
|
||||
self.filepath = filepath
|
||||
self.lines = source.splitlines()
|
||||
self.functions: List[Dict] = []
|
||||
|
||||
def _get_source_segment(self, start_lineno: int, end_lineno: int) -> str:
|
||||
"""Get source code from start to end line (1-indexed, inclusive)."""
|
||||
# AST end_lineno is inclusive
|
||||
start_idx = start_lineno - 1
|
||||
end_idx = end_lineno
|
||||
return '\n'.join(self.lines[start_idx:end_idx])
|
||||
|
||||
def visit(self, tree):
|
||||
"""Collect all function and async function definitions."""
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.FunctionDef) or isinstance(node, ast.AsyncFunctionDef):
|
||||
# Get the full source for this function including decorators
|
||||
start = node.lineno
|
||||
end = node.end_lineno
|
||||
body_source = self._get_source_segment(start, end)
|
||||
|
||||
# Also collect parent class name if this is a method
|
||||
class_name = None
|
||||
parent = node.parent if hasattr(node, 'parent') else None
|
||||
if parent and isinstance(parent, ast.ClassDef):
|
||||
class_name = parent.name
|
||||
|
||||
self.functions.append({
|
||||
'name': node.name,
|
||||
'file': self.filepath,
|
||||
'start_line': start,
|
||||
'end_line': end,
|
||||
'body': body_source,
|
||||
'class_name': class_name,
|
||||
'is_method': class_name is not None,
|
||||
})
|
||||
|
||||
|
||||
import ast
|
||||
|
||||
class ParentNodeVisitor(ast.NodeVisitor):
|
||||
"""Annotate nodes with parent references."""
|
||||
def __init__(self, parent=None):
|
||||
self.parent = parent
|
||||
|
||||
def generic_visit(self, node):
|
||||
node.parent = self.parent
|
||||
for child in ast.iter_child_nodes(node):
|
||||
self.__class__(child).parent = node
|
||||
super().generic_visit(node)
|
||||
|
||||
|
||||
def extract_functions_from_file(filepath: str) -> List[Dict]:
|
||||
"""Extract all function definitions from a Python file."""
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
||||
source = f.read()
|
||||
tree = ast.parse(source, filename=str(filepath))
|
||||
|
||||
# Annotate with parent references
|
||||
for node in ast.walk(tree):
|
||||
for child in ast.iter_child_nodes(node):
|
||||
child.parent = node
|
||||
|
||||
extractor = FunctionExtractor(source, str(filepath))
|
||||
extractor.visit(tree)
|
||||
return extractor.functions
|
||||
except (SyntaxError, UnicodeDecodeError, OSError) as e:
|
||||
return []
|
||||
|
||||
|
||||
def scan_directory(directory: str, extensions: Tuple[str, ...] = ('.py',)) -> List[Dict]:
|
||||
"""Scan directory for Python files and extract all functions."""
|
||||
all_functions = []
|
||||
path = Path(directory)
|
||||
|
||||
for filepath in path.rglob('*'):
|
||||
if filepath.is_file() and filepath.suffix in extensions:
|
||||
# Skip common non-source dirs
|
||||
parts = filepath.parts
|
||||
if any(ex in parts for ex in ('__pycache__', 'node_modules', '.git', 'venv', '.venv', 'dist', 'build')):
|
||||
continue
|
||||
if filepath.name.startswith('.'):
|
||||
continue
|
||||
|
||||
functions = extract_functions_from_file(str(filepath))
|
||||
all_functions.extend(functions)
|
||||
|
||||
return all_functions
|
||||
|
||||
|
||||
# ── Duplicate detection ─────────────────────────────────────────────────────
|
||||
|
||||
def find_duplicates(functions: List[Dict], similarity_threshold: float = 0.95) -> Dict:
|
||||
"""
|
||||
Find duplicate and near-duplicate functions.
|
||||
|
||||
Returns dict with:
|
||||
- exact_duplicates: {hash: [function_info, ...]}
|
||||
- near_duplicates: [[function_info, ...], ...]
|
||||
- stats: total_functions, unique_exact, exact_dupe_count, near_dupe_count
|
||||
"""
|
||||
# Phase 1: Exact duplicates by code hash
|
||||
hash_groups: Dict[str, List[Dict]] = defaultdict(list)
|
||||
for func in functions:
|
||||
h = code_hash(func['body'])
|
||||
hash_groups[h].append(func)
|
||||
|
||||
exact_duplicates = {h: group for h, group in hash_groups.items() if len(group) > 1}
|
||||
exact_dupe_count = sum(len(group) - 1 for group in exact_duplicates.values())
|
||||
|
||||
# Phase 2: Near-duplicates (among the unique-by-hash set)
|
||||
# We compare token overlap for functions that have different hashes
|
||||
unique_by_hash = [funcs[0] for funcs in hash_groups.values()]
|
||||
near_duplicate_groups = []
|
||||
|
||||
# Simple token-based similarity
|
||||
def tokenize(code: str) -> set:
|
||||
return set(re.findall(r'[a-zA-Z_][a-zA-Z0-9_]*', code.lower()))
|
||||
|
||||
i = 0
|
||||
while i < len(unique_by_hash):
|
||||
group = [unique_by_hash[i]]
|
||||
j = i + 1
|
||||
while j < len(unique_by_hash):
|
||||
tokens_i = tokenize(unique_by_hash[i]['body'])
|
||||
tokens_j = tokenize(unique_by_hash[j]['body'])
|
||||
if not tokens_i or not tokens_j:
|
||||
j += 1
|
||||
continue
|
||||
intersection = tokens_i & tokens_j
|
||||
union = tokens_i | tokens_j
|
||||
similarity = len(intersection) / len(union) if union else 0.0
|
||||
|
||||
if similarity >= similarity_threshold:
|
||||
group.append(unique_by_hash[j])
|
||||
unique_by_hash.pop(j)
|
||||
else:
|
||||
j += 1
|
||||
|
||||
if len(group) > 1:
|
||||
near_duplicate_groups.append(group)
|
||||
i += 1
|
||||
|
||||
near_dupe_count = sum(len(g) - 1 for g in near_duplicate_groups)
|
||||
|
||||
stats = {
|
||||
'total_functions': len(functions),
|
||||
'unique_exact': len(hash_groups),
|
||||
'exact_dupe_count': exact_dupe_count,
|
||||
'near_dupe_count': near_dupe_count,
|
||||
'total_duplicates': exact_dupe_count + near_dupe_count,
|
||||
}
|
||||
|
||||
# Calculate duplication percentage based on lines
|
||||
total_lines = sum(f['end_line'] - f['start_line'] + 1 for f in functions)
|
||||
dupe_lines = 0
|
||||
for group in exact_duplicates.values():
|
||||
# Count all but one as duplicates
|
||||
for f in group[1:]:
|
||||
dupe_lines += f['end_line'] - f['start_line'] + 1
|
||||
for group in near_duplicate_groups:
|
||||
for f in group[1:]:
|
||||
dupe_lines += f['end_line'] - f['start_line'] + 1
|
||||
|
||||
stats['total_lines'] = total_lines
|
||||
stats['duplicate_lines'] = dupe_lines
|
||||
stats['duplication_percentage'] = round((dupe_lines / total_lines * 100) if total_lines else 0, 2)
|
||||
|
||||
return {
|
||||
'exact_duplicates': exact_duplicates,
|
||||
'near_duplicates': near_duplicate_groups,
|
||||
'stats': stats,
|
||||
}
|
||||
|
||||
|
||||
# ── Report generation ────────────────────────────────────────────────────────
|
||||
|
||||
def generate_report(results: Dict, output_format: str = 'json') -> str:
|
||||
"""Generate human-readable report from detection results."""
|
||||
stats = results['stats']
|
||||
|
||||
if output_format == 'json':
|
||||
return json.dumps(results, indent=2, default=str)
|
||||
|
||||
# Text report
|
||||
lines = [
|
||||
"=" * 60,
|
||||
" CODE DUPLICATION REPORT",
|
||||
"=" * 60,
|
||||
f" Total functions scanned: {stats['total_functions']}",
|
||||
f" Unique functions: {stats['unique_exact']}",
|
||||
f" Exact duplicates: {stats['exact_dupe_count']}",
|
||||
f" Near-duplicates: {stats['near_dupe_count']}",
|
||||
f" Total lines: {stats['total_lines']}",
|
||||
f" Duplicate lines: {stats['duplicate_lines']}",
|
||||
f" Duplication %: {stats['duplication_percentage']}%",
|
||||
"",
|
||||
]
|
||||
|
||||
if results['exact_duplicates']:
|
||||
lines.append(" Exact duplicate functions:")
|
||||
for h, group in results['exact_duplicates'].items():
|
||||
first = group[0]
|
||||
lines.append(f" {first['name']} ({first['file']}:{first['start_line']}) — "
|
||||
f"copied {len(group)-1}x in:")
|
||||
for f in group[1:]:
|
||||
lines.append(f" → {f['file']}:{f['start_line']}")
|
||||
lines.append("")
|
||||
|
||||
if results['near_duplicates']:
|
||||
lines.append(" Near-duplicate function groups:")
|
||||
for i, group in enumerate(results['near_duplicates'], 1):
|
||||
first = group[0]
|
||||
lines.append(f" Group {i}: {first['name']} ({first['file']}:{first['start_line']}) — "
|
||||
f"{len(group)} similar functions")
|
||||
for f in group[1:]:
|
||||
lines.append(f" → {f['file']}:{f['start_line']}")
|
||||
lines.append("")
|
||||
|
||||
lines.append("=" * 60)
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
# ── CLI ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Code Duplication Detector")
|
||||
parser.add_argument('--directory', default='.',
|
||||
help='Directory to scan (default: current directory)')
|
||||
parser.add_argument('--output', help='Output file for JSON report')
|
||||
parser.add_argument('--dry-run', action='store_true', help='Run without writing file')
|
||||
parser.add_argument('--threshold', type=float, default=0.95,
|
||||
help='Similarity threshold for near-dupes (default: 0.95)')
|
||||
parser.add_argument('--json', action='store_true', help='JSON output to stdout')
|
||||
parser.add_argument('--test', action='store_true', help='Run built-in test')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.test:
|
||||
_run_test()
|
||||
return
|
||||
|
||||
# Scan
|
||||
functions = scan_directory(args.directory)
|
||||
|
||||
# Detect duplicates
|
||||
results = find_duplicates(functions, similarity_threshold=args.threshold)
|
||||
stats = results['stats']
|
||||
|
||||
# Output
|
||||
if args.json:
|
||||
print(json.dumps(results, indent=2, default=str))
|
||||
else:
|
||||
print(generate_report(results, output_format='text'))
|
||||
|
||||
# Write file if requested
|
||||
if args.output and not args.dry_run:
|
||||
os.makedirs(os.path.dirname(args.output) or '.', exist_ok=True)
|
||||
with open(args.output, 'w') as f:
|
||||
json.dump(results, f, indent=2, default=str)
|
||||
print(f"\nReport written to: {args.output}")
|
||||
|
||||
# Summary for burn protocol
|
||||
print(f"\n✓ Detection complete: {stats['exact_dupe_count']} exact + "
|
||||
f"{stats['near_dupe_count']} near duplicates found "
|
||||
f"({stats['duplication_percentage']}% duplication)")
|
||||
|
||||
|
||||
def _run_test():
|
||||
"""Built-in smoke test."""
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Create test files with duplicate code
|
||||
f1 = Path(tmpdir) / 'mod1.py'
|
||||
f1.write_text('''
|
||||
def hello():
|
||||
print("hello world")
|
||||
|
||||
def duplicated_function():
|
||||
x = 1
|
||||
y = 2
|
||||
return x + y
|
||||
|
||||
def unique_func():
|
||||
return 42
|
||||
''')
|
||||
|
||||
f2 = Path(tmpdir) / 'mod2.py'
|
||||
f2.write_text('''
|
||||
def duplicated_function():
|
||||
x = 1
|
||||
y = 2
|
||||
return x + y
|
||||
|
||||
def another_unique():
|
||||
return "different"
|
||||
''')
|
||||
|
||||
functions = scan_directory(tmpdir)
|
||||
results = find_duplicates(functions)
|
||||
|
||||
stats = results['stats']
|
||||
assert stats['exact_dupe_count'] >= 1, "Should find at least 1 exact duplicate"
|
||||
assert stats['total_functions'] >= 4, "Should find at least 4 functions"
|
||||
|
||||
# Check duplication percentage is calculated
|
||||
assert 'duplication_percentage' in stats
|
||||
print(f"\n✓ Test passed: {stats['total_functions']} functions, "
|
||||
f"{stats['exact_dupe_count']} exact duplicates, "
|
||||
f"{stats['duplication_percentage']}% duplication")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,375 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Cross-Repo Connector — Issue Reference Scanner (8.4)
|
||||
|
||||
Scans all repos in an org for cross-repo issue references. Identifies
|
||||
connections where one repo's issue/PR references another repo's issue number.
|
||||
|
||||
This is the smallest concrete first step toward full cross-repo analysis.
|
||||
Future: extend to code imports, similar patterns.
|
||||
|
||||
Usage:
|
||||
python3 scripts/cross_repo_connector.py --org Timmy_Foundation --dry-run
|
||||
python3 scripts/cross_repo_connector.py --org Timmy_Foundation --commit
|
||||
|
||||
Output:
|
||||
knowledge/global/cross_repo_connections.yaml — new facts
|
||||
knowledge/index.json — updated index
|
||||
metrics/cross_repo_report.md — human-readable summary
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
|
||||
GITEA_URL = "https://forge.alexanderwhitestone.com"
|
||||
|
||||
|
||||
# ── GiteaClient (lifted from priority_rebalancer.py, minimal subset) ──────
|
||||
|
||||
class GiteaClient:
|
||||
def __init__(self, base_url: str, token: str):
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.token = token
|
||||
|
||||
def _request(self, path: str, params: dict = None) -> any:
|
||||
url = f"{self.base_url}/api/v1{path}"
|
||||
if params:
|
||||
qs = "&".join(f"{k}={v}" for k, v in params.items() if v is not None)
|
||||
url += f"?{qs}"
|
||||
req = urllib.request.Request(url)
|
||||
req.add_header("Authorization", f"token {self.token}")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"API error {e.code} for {path}: {e.read().decode()[:200]}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def get_org_repos(self, org: str) -> List[dict]:
|
||||
repos = []
|
||||
page = 1
|
||||
while True:
|
||||
batch = self._request(f"/orgs/{org}/repos", {"limit": 50, "page": page})
|
||||
if not batch:
|
||||
break
|
||||
repos.extend(batch)
|
||||
if len(batch) < 50:
|
||||
break
|
||||
page += 1
|
||||
return repos
|
||||
|
||||
def get_issues(self, org: str, repo: str, state: str = "closed") -> List[dict]:
|
||||
"""Fetch issues (and PRs, which Gitea returns together) for a repo."""
|
||||
issues = []
|
||||
page = 1
|
||||
while True:
|
||||
batch = self._request(
|
||||
f"/repos/{org}/{repo}/issues",
|
||||
{"state": state, "limit": 50, "page": page, "type": "issues"}
|
||||
)
|
||||
if not batch:
|
||||
break
|
||||
issues.extend(batch)
|
||||
if len(batch) < 50:
|
||||
break
|
||||
page += 1
|
||||
return issues
|
||||
|
||||
def get_issue(self, org: str, repo: str, issue_num: int) -> Optional[dict]:
|
||||
return self._request(f"/repos/{org}/{repo}/issues/{issue_num}")
|
||||
|
||||
|
||||
# ── Token handling ──────────────────────────────────────────────────────────
|
||||
|
||||
def get_token() -> str:
|
||||
"""Read Gitea token from ~/.config/gitea/token or $GITEA_TOKEN."""
|
||||
token_path = Path.home() / ".config" / "gitea" / "token"
|
||||
if token_path.exists():
|
||||
return token_path.read_text().strip()
|
||||
env_token = os.environ.get("GITEA_TOKEN", "")
|
||||
if env_token:
|
||||
return env_token
|
||||
raise FileNotFoundError(
|
||||
"Gitea token not found. Create ~/.config/gitea/token or set $GITEA_TOKEN."
|
||||
)
|
||||
|
||||
|
||||
# ── Cross-reference extraction ──────────────────────────────────────────────
|
||||
|
||||
# Patterns that reference another repo's issue/PR:
|
||||
# - Timmy_Foundation/other-repo#123
|
||||
# - Timmy_Foundation/other-repo/issues/123
|
||||
# - full URL: https://forge.../Timmy_Foundation/other-repo/issues/123
|
||||
# - just "#123" — needs resolving via linked issue numbers (more complex, skip for v1)
|
||||
|
||||
CROSS_REF_PATTERN = re.compile(
|
||||
r"(?:Timmy_Foundation/([\w.-]+)#(\d+))"
|
||||
r"|(?:Timmy_Foundation/([\w.-]+)/issues/(\d+))"
|
||||
r"|(?:https?://[^/]+/Timmy_Foundation/([\w.-]+)/issues/(\d+))"
|
||||
)
|
||||
|
||||
|
||||
def extract_cross_repo_refs(text: str, own_repo: str) -> List[Tuple[str, int]]:
|
||||
"""
|
||||
Return list of (other_repo, issue_number) tuples found in text.
|
||||
Excludes references to the same repo.
|
||||
"""
|
||||
matches = []
|
||||
for m in CROSS_REF_PATTERN.finditer(text or ""):
|
||||
repo = m.group(1) or m.group(3) or m.group(5)
|
||||
num = m.group(2) or m.group(4) or m.group(6)
|
||||
if repo and num:
|
||||
repo = repo.lower().replace("_", "-")
|
||||
if repo != own_repo.lower().replace("_", "-"):
|
||||
matches.append((repo, int(num)))
|
||||
return matches
|
||||
|
||||
|
||||
# ── Knowledge store helpers ──────────────────────────────────────────────────
|
||||
|
||||
def load_index(index_path: Path) -> dict:
|
||||
if index_path.exists():
|
||||
with index_path.open() as f:
|
||||
return json.load(f)
|
||||
return {"version": 1, "last_updated": "", "total_facts": 0, "facts": []}
|
||||
|
||||
|
||||
def save_index(index: dict, index_path: Path) -> None:
|
||||
index["version"] = 1
|
||||
index["last_updated"] = datetime.now(timezone.utc).isoformat()
|
||||
index["total_facts"] = len(index["facts"])
|
||||
with index_path.open("w") as f:
|
||||
json.dump(index, f, indent=2, sort_keys=True, ensure_ascii=False)
|
||||
|
||||
|
||||
def generate_fact_id(domain: str, category: str, sequence: int) -> str:
|
||||
return f"{domain}:{category}:{sequence:03d}"
|
||||
|
||||
|
||||
def make_connection_fact(
|
||||
source_repo: str,
|
||||
target_repo: str,
|
||||
issue_num: int,
|
||||
source_title: str,
|
||||
target_title: Optional[str] = None,
|
||||
) -> dict:
|
||||
"""Create a cross-repo connection fact."""
|
||||
fact_text = (
|
||||
f"{source_repo} references {target_repo} via issue #{issue_num}: "
|
||||
f"{source_title[:100]}"
|
||||
)
|
||||
if target_title:
|
||||
fact_text += f" → [{target_repo}#{issue_num}] {target_title[:100]}"
|
||||
return {
|
||||
"id": "cross-repo:connection:TBD", # filled by caller with seq
|
||||
"fact": fact_text,
|
||||
"category": "pattern", # cross-repo reference is a discovered pattern
|
||||
"domain": "global", # applies org-wide
|
||||
"confidence": 0.9,
|
||||
"tags": ["cross-repo", "issue-reference", "connection"],
|
||||
"source_count": 1,
|
||||
"first_seen": datetime.now(timezone.utc).isoformat(),
|
||||
"last_confirmed": datetime.now(timezone.utc).isoformat(),
|
||||
"related": [],
|
||||
}
|
||||
|
||||
|
||||
def append_to_global_yaml(facts: List[dict], knowledge_dir: Path) -> None:
|
||||
"""Append new cross-repo facts to knowledge/global/cross_repo.yaml."""
|
||||
global_dir = knowledge_dir / "global"
|
||||
global_dir.mkdir(parents=True, exist_ok=True)
|
||||
yaml_path = global_dir / "cross_repo.yaml"
|
||||
|
||||
# Load existing YAML documents (may be dict or list-of-dict)
|
||||
existing_docs = []
|
||||
if yaml_path.exists():
|
||||
with yaml_path.open() as f:
|
||||
try:
|
||||
import yaml
|
||||
existing_docs = list(yaml.safe_load_all(f)) or []
|
||||
except Exception:
|
||||
existing_docs = []
|
||||
|
||||
# Normalize to dict documents (unwrap single-element lists)
|
||||
normalized = []
|
||||
for doc in existing_docs:
|
||||
if isinstance(doc, dict):
|
||||
normalized.append(doc)
|
||||
elif isinstance(doc, list) and len(doc) == 1 and isinstance(doc[0], dict):
|
||||
normalized.append(doc[0])
|
||||
|
||||
# Determine next sequence number
|
||||
max_seq = 0
|
||||
for doc in normalized:
|
||||
for f_item in doc.get("facts", []):
|
||||
fid = f_item.get("id", "")
|
||||
if fid.startswith("cross-repo:"):
|
||||
try:
|
||||
seq = int(fid.split(":")[-1])
|
||||
max_seq = max(max_seq, seq)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Assign new IDs
|
||||
new_facts = []
|
||||
for i, fact in enumerate(facts, start=max_seq + 1):
|
||||
fact_copy = dict(fact) # don't mutate caller's
|
||||
fact_copy["id"] = f"cross-repo:connection:{i:03d}"
|
||||
new_facts.append(fact_copy)
|
||||
|
||||
if not new_facts:
|
||||
return
|
||||
|
||||
# New YAML document (dict, not list)
|
||||
new_doc = {
|
||||
"domain": "global",
|
||||
"category": "pattern",
|
||||
"version": 1,
|
||||
"last_updated": datetime.now(timezone.utc).isoformat(),
|
||||
"facts": new_facts,
|
||||
}
|
||||
|
||||
# Append document
|
||||
with yaml_path.open("a") as f:
|
||||
f.write("\n---\n")
|
||||
import yaml
|
||||
yaml.dump(new_doc, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
print(f" Appended {len(new_facts)} facts to {yaml_path}")
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Cross-repo issue reference connector")
|
||||
p.add_argument("--org", default="Timmy_Foundation", help="Org to scan")
|
||||
p.add_argument("--dry-run", action="store_true", help="Don't write knowledge files")
|
||||
p.add_argument("--state", default="closed", choices=["open", "closed", "all"],
|
||||
help="Issue state to scan (default: closed)")
|
||||
p.add_argument("--limit-repos", type=int, help="Max repos to process (dev)")
|
||||
p.add_argument("--limit-issues", type=int, help="Max issues per repo (dev)")
|
||||
args = p.parse_args()
|
||||
|
||||
token = get_token()
|
||||
client = GiteaClient(GITEA_URL, token)
|
||||
knowledge_dir = Path("knowledge")
|
||||
index_path = knowledge_dir / "index.json"
|
||||
|
||||
print(f"Fetching org repos…")
|
||||
repos = client.get_org_repos(args.org)
|
||||
print(f"Found {len(repos)} repos in {args.org}")
|
||||
|
||||
if args.limit_repos:
|
||||
repos = repos[:args.limit_repos]
|
||||
print(f" (limited to {args.limit_repos})")
|
||||
|
||||
# Build repo name -> info map
|
||||
repo_info = {r["name"].lower(): r for r in repos}
|
||||
|
||||
# Step 1: collect all cross-repo references from issues
|
||||
# ((source_repo, target_repo, issue_num, source_title))
|
||||
raw_connections = []
|
||||
for repo_meta in repos:
|
||||
repo_name = repo_meta["name"]
|
||||
print(f"\nScanning {repo_name} issues…")
|
||||
issues = client.get_issues(args.org, repo_name, state=args.state)
|
||||
print(f" {len(issues)} {args.state} issues")
|
||||
if args.limit_issues:
|
||||
issues = issues[:args.limit_issues]
|
||||
|
||||
for issue in issues:
|
||||
body = issue.get("body", "") or ""
|
||||
title = issue.get("title", "") or ""
|
||||
refs = extract_cross_repo_refs(body + "\n" + title, repo_name)
|
||||
for target_repo, num in refs:
|
||||
raw_connections.append((repo_name, target_repo, num, title))
|
||||
|
||||
print(f"\nFound {len(raw_connections)} cross-repo reference(s).")
|
||||
|
||||
# Deduplicate: (source_repo, target_repo, issue_num) → best title
|
||||
connection_map = {} # (src, tgt, num) → title
|
||||
for src, tgt, num, title in raw_connections:
|
||||
key = (src.lower(), tgt.lower(), num)
|
||||
if key not in connection_map or len(title) > len(connection_map[key]):
|
||||
connection_map[key] = title
|
||||
|
||||
print(f"Unique connections: {len(connection_map)}")
|
||||
|
||||
if args.dry_run:
|
||||
print("\nDry-run — not writing knowledge files.")
|
||||
print("\nDiscovered connections:")
|
||||
for (src, tgt, num), title in sorted(connection_map.items()):
|
||||
print(f" {src} → {tgt}#{num}: {title[:80]}")
|
||||
return 0
|
||||
|
||||
# Step 2: For each unique connection, try to resolve target issue title
|
||||
# to enrich the fact text
|
||||
resolved_facts = []
|
||||
for (src, tgt, num), src_title in sorted(connection_map.items()):
|
||||
target_title = None
|
||||
try:
|
||||
target_issue = client.get_issue(args.org, tgt, num)
|
||||
if target_issue:
|
||||
target_title = target_issue.get("title", "")
|
||||
except Exception as e:
|
||||
print(f" Could not fetch {tgt}#{num}: {e}", file=sys.stderr)
|
||||
|
||||
fact = make_connection_fact(src, tgt, num, src_title, target_title)
|
||||
# Temporary ID — will be assigned when we know sequence
|
||||
resolved_facts.append(fact)
|
||||
|
||||
# Step 3: Update index.json
|
||||
index = load_index(index_path)
|
||||
next_seq = max(
|
||||
[int(f["id"].split(":")[-1]) for f in index["facts"]
|
||||
if f["id"].startswith("cross-repo:")]
|
||||
) + 1 if any(f["id"].startswith("cross-repo:") for f in index["facts"]) else 1
|
||||
|
||||
for i, fact in enumerate(resolved_facts):
|
||||
fact["id"] = f"cross-repo:connection:{next_seq + i:03d}"
|
||||
index["facts"].append(fact)
|
||||
|
||||
save_index(index, index_path)
|
||||
print(f"\n✓ Updated knowledge/index.json (+{len(resolved_facts)} facts)")
|
||||
|
||||
# Step 4: Write to global YAML
|
||||
append_to_global_yaml(resolved_facts, knowledge_dir)
|
||||
|
||||
# Step 5: Generate metrics/report
|
||||
metrics_dir = Path("metrics")
|
||||
metrics_dir.mkdir(exist_ok=True)
|
||||
report_path = metrics_dir / "cross_repo_report.md"
|
||||
with report_path.open("w") as f:
|
||||
f.write(f"# Cross-Repo Connection Report\n\n")
|
||||
f.write(f"Generated: {datetime.now(timezone.utc).isoformat()}\n")
|
||||
f.write(f"Org: {args.org}\n")
|
||||
f.write(f"Repos scanned: {len(repos)}\n")
|
||||
f.write(f"\n## Connections\n\n")
|
||||
by_source = defaultdict(list)
|
||||
for fact in resolved_facts:
|
||||
src = fact["fact"].split(" ")[0]
|
||||
by_source[src].append(fact)
|
||||
for src in sorted(by_source.keys()):
|
||||
f.write(f"### {src}\n\n")
|
||||
for fact in by_source[src]:
|
||||
f.write(f"- {fact['fact']}\n")
|
||||
f.write("\n")
|
||||
f.write(f"\nTotal connections: {len(resolved_facts)}\n")
|
||||
|
||||
print(f"✓ Wrote {report_path}")
|
||||
|
||||
print(f"\nDone. Next: run validation:")
|
||||
print(f" python3 scripts/validate_knowledge.py")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
351
scripts/pr_complexity_scorer.py
Normal file
351
scripts/pr_complexity_scorer.py
Normal file
@@ -0,0 +1,351 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PR Complexity Scorer - Estimate review effort for PRs.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass, asdict
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
GITEA_BASE = "https://forge.alexanderwhitestone.com/api/v1"
|
||||
|
||||
DEPENDENCY_FILES = {
|
||||
"requirements.txt", "pyproject.toml", "setup.py", "setup.cfg",
|
||||
"Pipfile", "poetry.lock", "package.json", "yarn.lock", "Gemfile",
|
||||
"go.mod", "Cargo.toml", "pom.xml", "build.gradle"
|
||||
}
|
||||
|
||||
TEST_PATTERNS = [
|
||||
r"tests?/.*\.py$", r".*_test\.py$", r"test_.*\.py$",
|
||||
r"spec/.*\.rb$", r".*_spec\.rb$",
|
||||
r"__tests__/", r".*\.test\.(js|ts|jsx|tsx)$"
|
||||
]
|
||||
|
||||
WEIGHT_FILES = 0.25
|
||||
WEIGHT_LINES = 0.25
|
||||
WEIGHT_DEPS = 0.30
|
||||
WEIGHT_TEST_COV = 0.20
|
||||
|
||||
SMALL_FILES = 5
|
||||
MEDIUM_FILES = 20
|
||||
LARGE_FILES = 50
|
||||
|
||||
SMALL_LINES = 100
|
||||
MEDIUM_LINES = 500
|
||||
LARGE_LINES = 2000
|
||||
|
||||
TIME_PER_POINT = {1: 5, 2: 10, 3: 15, 4: 20, 5: 25, 6: 30, 7: 45, 8: 60, 9: 90, 10: 120}
|
||||
|
||||
|
||||
@dataclass
|
||||
class PRComplexity:
|
||||
pr_number: int
|
||||
title: str
|
||||
files_changed: int
|
||||
additions: int
|
||||
deletions: int
|
||||
has_dependency_changes: bool
|
||||
test_coverage_delta: Optional[int]
|
||||
score: int
|
||||
estimated_minutes: int
|
||||
reasons: List[str]
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
class GiteaClient:
|
||||
def __init__(self, token: str):
|
||||
self.token = token
|
||||
self.base_url = GITEA_BASE.rstrip("/")
|
||||
|
||||
def _request(self, path: str, params: Dict = None) -> Any:
|
||||
url = f"{self.base_url}{path}"
|
||||
if params:
|
||||
qs = "&".join(f"{k}={v}" for k, v in params.items() if v is not None)
|
||||
url += f"?{qs}"
|
||||
|
||||
req = urllib.request.Request(url)
|
||||
req.add_header("Authorization", f"token {self.token}")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"API error {e.code}: {e.read().decode()[:200]}", file=sys.stderr)
|
||||
return None
|
||||
except urllib.error.URLError as e:
|
||||
print(f"Network error: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def get_open_prs(self, org: str, repo: str) -> List[Dict]:
|
||||
prs = []
|
||||
page = 1
|
||||
while True:
|
||||
batch = self._request(f"/repos/{org}/{repo}/pulls", {"limit": 50, "page": page, "state": "open"})
|
||||
if not batch:
|
||||
break
|
||||
prs.extend(batch)
|
||||
if len(batch) < 50:
|
||||
break
|
||||
page += 1
|
||||
return prs
|
||||
|
||||
def get_pr_files(self, org: str, repo: str, pr_number: int) -> List[Dict]:
|
||||
files = []
|
||||
page = 1
|
||||
while True:
|
||||
batch = self._request(
|
||||
f"/repos/{org}/{repo}/pulls/{pr_number}/files",
|
||||
{"limit": 100, "page": page}
|
||||
)
|
||||
if not batch:
|
||||
break
|
||||
files.extend(batch)
|
||||
if len(batch) < 100:
|
||||
break
|
||||
page += 1
|
||||
return files
|
||||
|
||||
def post_comment(self, org: str, repo: str, pr_number: int, body: str) -> bool:
|
||||
data = json.dumps({"body": body}).encode("utf-8")
|
||||
req = urllib.request.Request(
|
||||
f"{self.base_url}/repos/{org}/{repo}/issues/{pr_number}/comments",
|
||||
data=data,
|
||||
method="POST",
|
||||
headers={"Authorization": f"token {self.token}", "Content-Type": "application/json"}
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return resp.status in (200, 201)
|
||||
except urllib.error.HTTPError:
|
||||
return False
|
||||
|
||||
|
||||
def is_dependency_file(filename: str) -> bool:
|
||||
return any(filename.endswith(dep) for dep in DEPENDENCY_FILES)
|
||||
|
||||
|
||||
def is_test_file(filename: str) -> bool:
|
||||
return any(re.search(pattern, filename) for pattern in TEST_PATTERNS)
|
||||
|
||||
|
||||
def score_pr(
|
||||
files_changed: int,
|
||||
additions: int,
|
||||
deletions: int,
|
||||
has_dependency_changes: bool,
|
||||
test_coverage_delta: Optional[int] = None
|
||||
) -> tuple[int, int, List[str]]:
|
||||
score = 1.0
|
||||
reasons = []
|
||||
|
||||
# Files changed
|
||||
if files_changed <= SMALL_FILES:
|
||||
fscore = 1.0
|
||||
reasons.append("small number of files changed")
|
||||
elif files_changed <= MEDIUM_FILES:
|
||||
fscore = 2.0
|
||||
reasons.append("moderate number of files changed")
|
||||
elif files_changed <= LARGE_FILES:
|
||||
fscore = 2.5
|
||||
reasons.append("large number of files changed")
|
||||
else:
|
||||
fscore = 3.0
|
||||
reasons.append("very large PR spanning many files")
|
||||
|
||||
# Lines changed
|
||||
total_lines = additions + deletions
|
||||
if total_lines <= SMALL_LINES:
|
||||
lscore = 1.0
|
||||
reasons.append("small change size")
|
||||
elif total_lines <= MEDIUM_LINES:
|
||||
lscore = 2.0
|
||||
reasons.append("moderate change size")
|
||||
elif total_lines <= LARGE_LINES:
|
||||
lscore = 3.0
|
||||
reasons.append("large change size")
|
||||
else:
|
||||
lscore = 4.0
|
||||
reasons.append("very large change")
|
||||
|
||||
# Dependency changes
|
||||
if has_dependency_changes:
|
||||
dscore = 2.5
|
||||
reasons.append("dependency changes (architectural impact)")
|
||||
else:
|
||||
dscore = 0.0
|
||||
|
||||
# Test coverage delta
|
||||
tscore = 0.0
|
||||
if test_coverage_delta is not None:
|
||||
if test_coverage_delta > 0:
|
||||
reasons.append(f"test additions (+{test_coverage_delta} test files)")
|
||||
tscore = -min(2.0, test_coverage_delta / 2.0)
|
||||
elif test_coverage_delta < 0:
|
||||
reasons.append(f"test removals ({abs(test_coverage_delta)} test files)")
|
||||
tscore = min(2.0, abs(test_coverage_delta) * 0.5)
|
||||
else:
|
||||
reasons.append("test coverage change not assessed")
|
||||
|
||||
# Weighted sum, scaled by 3 to use full 1-10 range
|
||||
bonus = (fscore * WEIGHT_FILES) + (lscore * WEIGHT_LINES) + (dscore * WEIGHT_DEPS) + (tscore * WEIGHT_TEST_COV)
|
||||
scaled_bonus = bonus * 3.0
|
||||
score = 1.0 + scaled_bonus
|
||||
|
||||
final_score = max(1, min(10, int(round(score))))
|
||||
est_minutes = TIME_PER_POINT.get(final_score, 30)
|
||||
|
||||
return final_score, est_minutes, reasons
|
||||
|
||||
|
||||
def analyze_pr(client: GiteaClient, org: str, repo: str, pr_data: Dict) -> PRComplexity:
|
||||
pr_num = pr_data["number"]
|
||||
title = pr_data.get("title", "")
|
||||
files = client.get_pr_files(org, repo, pr_num)
|
||||
|
||||
additions = sum(f.get("additions", 0) for f in files)
|
||||
deletions = sum(f.get("deletions", 0) for f in files)
|
||||
filenames = [f.get("filename", "") for f in files]
|
||||
|
||||
has_deps = any(is_dependency_file(f) for f in filenames)
|
||||
|
||||
test_added = sum(1 for f in files if f.get("status") == "added" and is_test_file(f.get("filename", "")))
|
||||
test_removed = sum(1 for f in files if f.get("status") == "removed" and is_test_file(f.get("filename", "")))
|
||||
test_delta = test_added - test_removed if (test_added or test_removed) else None
|
||||
|
||||
score, est_min, reasons = score_pr(
|
||||
files_changed=len(files),
|
||||
additions=additions,
|
||||
deletions=deletions,
|
||||
has_dependency_changes=has_deps,
|
||||
test_coverage_delta=test_delta
|
||||
)
|
||||
|
||||
return PRComplexity(
|
||||
pr_number=pr_num,
|
||||
title=title,
|
||||
files_changed=len(files),
|
||||
additions=additions,
|
||||
deletions=deletions,
|
||||
has_dependency_changes=has_deps,
|
||||
test_coverage_delta=test_delta,
|
||||
score=score,
|
||||
estimated_minutes=est_min,
|
||||
reasons=reasons
|
||||
)
|
||||
|
||||
|
||||
def build_comment(complexity: PRComplexity) -> str:
|
||||
change_desc = f"{complexity.files_changed} files, +{complexity.additions}/-{complexity.deletions} lines"
|
||||
deps_note = "\n- :warning: Dependency changes detected — architectural review recommended" if complexity.has_dependency_changes else ""
|
||||
test_note = ""
|
||||
if complexity.test_coverage_delta is not None:
|
||||
if complexity.test_coverage_delta > 0:
|
||||
test_note = f"\n- :+1: {complexity.test_coverage_delta} test file(s) added"
|
||||
elif complexity.test_coverage_delta < 0:
|
||||
test_note = f"\n- :warning: {abs(complexity.test_coverage_delta)} test file(s) removed"
|
||||
|
||||
comment = f"## 📊 PR Complexity Analysis\n\n"
|
||||
comment += f"**PR #{complexity.pr_number}: {complexity.title}**\n\n"
|
||||
comment += f"| Metric | Value |\n|--------|-------|\n"
|
||||
comment += f"| Changes | {change_desc} |\n"
|
||||
comment += f"| Complexity Score | **{complexity.score}/10** |\n"
|
||||
comment += f"| Estimated Review Time | ~{complexity.estimated_minutes} minutes |\n\n"
|
||||
comment += f"### Scoring rationale:"
|
||||
for r in complexity.reasons:
|
||||
comment += f"\n- {r}"
|
||||
if deps_note:
|
||||
comment += deps_note
|
||||
if test_note:
|
||||
comment += test_note
|
||||
comment += f"\n\n---\n"
|
||||
comment += f"*Generated by PR Complexity Scorer — [issue #135](https://forge.alexanderwhitestone.com/Timmy_Foundation/compounding-intelligence/issues/135)*"
|
||||
return comment
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="PR Complexity Scorer")
|
||||
parser.add_argument("--org", default="Timmy_Foundation")
|
||||
parser.add_argument("--repo", default="compounding-intelligence")
|
||||
parser.add_argument("--token", default=os.environ.get("GITEA_TOKEN") or os.path.expanduser("~/.config/gitea/token"))
|
||||
parser.add_argument("--dry-run", action="store_true")
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
parser.add_argument("--output", default="metrics/pr_complexity.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
token_path = args.token
|
||||
if os.path.exists(token_path):
|
||||
with open(token_path) as f:
|
||||
token = f.read().strip()
|
||||
else:
|
||||
token = args.token
|
||||
|
||||
if not token:
|
||||
print("ERROR: No Gitea token provided", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
client = GiteaClient(token)
|
||||
|
||||
print(f"Fetching open PRs for {args.org}/{args.repo}...")
|
||||
prs = client.get_open_prs(args.org, args.repo)
|
||||
if not prs:
|
||||
print("No open PRs found.")
|
||||
sys.exit(0)
|
||||
|
||||
print(f"Found {len(prs)} open PR(s). Analyzing...")
|
||||
|
||||
results = []
|
||||
Path(args.output).parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for pr in prs:
|
||||
pr_num = pr["number"]
|
||||
title = pr.get("title", "")
|
||||
print(f" Analyzing PR #{pr_num}: {title[:60]}")
|
||||
|
||||
try:
|
||||
complexity = analyze_pr(client, args.org, args.repo, pr)
|
||||
results.append(complexity.to_dict())
|
||||
|
||||
comment = build_comment(complexity)
|
||||
|
||||
if args.dry_run:
|
||||
print(f" → Score: {complexity.score}/10, Est: {complexity.estimated_minutes}min [DRY-RUN]")
|
||||
elif args.apply:
|
||||
success = client.post_comment(args.org, args.repo, pr_num, comment)
|
||||
status = "[commented]" if success else "[FAILED]"
|
||||
print(f" → Score: {complexity.score}/10, Est: {complexity.estimated_minutes}min {status}")
|
||||
else:
|
||||
print(f" → Score: {complexity.score}/10, Est: {complexity.estimated_minutes}min [no action]")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ERROR analyzing PR #{pr_num}: {e}", file=sys.stderr)
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump({
|
||||
"org": args.org,
|
||||
"repo": args.repo,
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"pr_count": len(results),
|
||||
"results": results
|
||||
}, f, indent=2)
|
||||
|
||||
if results:
|
||||
scores = [r["score"] for r in results]
|
||||
print(f"\nResults saved to {args.output}")
|
||||
print(f"Summary: {len(results)} PRs, scores range {min(scores):.0f}-{max(scores):.0f}")
|
||||
else:
|
||||
print("\nNo results to save.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
168
scripts/test_code_duplication_detector.py
Normal file
168
scripts/test_code_duplication_detector.py
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Smoke test for code duplication detector — verifies:
|
||||
- Function extraction from Python files
|
||||
- Exact duplicate detection
|
||||
- Near-duplicate detection (token similarity)
|
||||
- Report generation and stats
|
||||
- JSON output format
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).parent.absolute()
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from code_duplication_detector import (
|
||||
extract_functions_from_file,
|
||||
scan_directory,
|
||||
find_duplicates,
|
||||
generate_report,
|
||||
)
|
||||
|
||||
|
||||
def test_extract_functions():
|
||||
"""Test that function extraction works."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
test_file = Path(tmpdir) / 'sample.py'
|
||||
test_file.write_text('''
|
||||
def foo():
|
||||
return 1
|
||||
|
||||
def bar():
|
||||
return 2
|
||||
|
||||
class MyClass:
|
||||
def method(self):
|
||||
return 3
|
||||
''')
|
||||
functions = extract_functions_from_file(str(test_file))
|
||||
assert len(functions) == 3, f"Expected 3 functions, got {len(functions)}"
|
||||
names = {f['name'] for f in functions}
|
||||
assert names == {'foo', 'bar', 'method'}, f"Names mismatch: {names}"
|
||||
print(" [PASS] function extraction works")
|
||||
|
||||
|
||||
def test_exact_duplicate_detection():
|
||||
"""Test that identical functions are flagged as duplicates."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Create two files with the same function
|
||||
f1 = Path(tmpdir) / 'a.py'
|
||||
f1.write_text('''
|
||||
def duplicated():
|
||||
x = 1
|
||||
y = 2
|
||||
return x + y
|
||||
''')
|
||||
f2 = Path(tmpdir) / 'b.py'
|
||||
f2.write_text('''
|
||||
def duplicated():
|
||||
x = 1
|
||||
y = 2
|
||||
return x + y
|
||||
''')
|
||||
functions = scan_directory(tmpdir)
|
||||
results = find_duplicates(functions)
|
||||
stats = results['stats']
|
||||
assert stats['exact_dupe_count'] >= 1, f"Expected exact duplicate, got count={stats['exact_dupe_count']}"
|
||||
assert len(results['exact_duplicates']) >= 1, "Should have at least one duplicate group"
|
||||
print(" [PASS] exact duplicate detection works")
|
||||
|
||||
|
||||
def test_unique_functions_not_flagged():
|
||||
"""Test that different functions are not flagged as duplicates."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
f1 = Path(tmpdir) / 'a.py'
|
||||
f1.write_text('def func_a(): return 1')
|
||||
f2 = Path(tmpdir) / 'b.py'
|
||||
f2.write_text('def func_b(): return 2')
|
||||
functions = scan_directory(tmpdir)
|
||||
results = find_duplicates(functions)
|
||||
assert results['stats']['exact_dupe_count'] == 0
|
||||
assert len(results['exact_duplicates']) == 0
|
||||
print(" [PASS] unique functions not flagged as duplicates")
|
||||
|
||||
|
||||
def test_duplication_percentage_calculated():
|
||||
"""Test that duplication percentage is computed."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Create file with mostly duplicated content
|
||||
f1 = Path(tmpdir) / 'a.py'
|
||||
f1.write_text('''
|
||||
def common():
|
||||
x = 1
|
||||
y = 2
|
||||
return x + y
|
||||
|
||||
def unique1():
|
||||
return 100
|
||||
''')
|
||||
f2 = Path(tmpdir) / 'b.py'
|
||||
f2.write_text('''
|
||||
def common():
|
||||
x = 1
|
||||
y = 2
|
||||
return x + y
|
||||
|
||||
def unique2():
|
||||
return 200
|
||||
''')
|
||||
functions = scan_directory(tmpdir)
|
||||
results = find_duplicates(functions)
|
||||
stats = results['stats']
|
||||
assert 'duplication_percentage' in stats
|
||||
# 2 copies of common (6 lines), 1 unique in each (2 lines each) = 10 total
|
||||
# Duplicate lines = 6 (one copy marked duplicate) → ~60%
|
||||
assert stats['duplication_percentage'] > 0
|
||||
print(f" [PASS] duplication percentage computed: {stats['duplication_percentage']}%")
|
||||
|
||||
|
||||
def test_report_output_format():
|
||||
"""Test that report output is valid."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
f1 = Path(tmpdir) / 'a.py'
|
||||
f1.write_text('def dup(): return 1')
|
||||
f2 = Path(tmpdir) / 'b.py'
|
||||
f2.write_text('def dup(): return 1')
|
||||
functions = scan_directory(tmpdir)
|
||||
results = find_duplicates(functions)
|
||||
|
||||
# Text report
|
||||
text = generate_report(results, output_format='text')
|
||||
assert 'CODE DUPLICATION REPORT' in text
|
||||
assert 'Total functions' in text
|
||||
print(" [PASS] text report format valid")
|
||||
|
||||
# JSON report
|
||||
json_out = generate_report(results, output_format='json')
|
||||
data = json.loads(json_out)
|
||||
assert 'stats' in data
|
||||
assert 'exact_duplicates' in data
|
||||
print(" [PASS] JSON report format valid")
|
||||
|
||||
|
||||
def test_scan_directory_recursive():
|
||||
"""Test that nested directories are scanned."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
subdir = Path(tmpdir) / 'sub'
|
||||
subdir.mkdir()
|
||||
(subdir / 'nested.py').write_text('def nested(): pass')
|
||||
(Path(tmpdir) / 'root.py').write_text('def root(): pass')
|
||||
functions = scan_directory(tmpdir)
|
||||
names = {f['name'] for f in functions}
|
||||
assert 'nested' in names and 'root' in names
|
||||
print(" [PASS] recursive directory scanning works")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Running code duplication detector smoke tests...")
|
||||
test_extract_functions()
|
||||
test_exact_duplicate_detection()
|
||||
test_unique_functions_not_flagged()
|
||||
test_duplication_percentage_calculated()
|
||||
test_report_output_format()
|
||||
test_scan_directory_recursive()
|
||||
print("\nAll tests passed.")
|
||||
170
scripts/test_pr_complexity_scorer.py
Normal file
170
scripts/test_pr_complexity_scorer.py
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tests for PR Complexity Scorer — unit tests for the scoring logic.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from pr_complexity_scorer import (
|
||||
score_pr,
|
||||
is_dependency_file,
|
||||
is_test_file,
|
||||
TIME_PER_POINT,
|
||||
SMALL_FILES,
|
||||
MEDIUM_FILES,
|
||||
LARGE_FILES,
|
||||
SMALL_LINES,
|
||||
MEDIUM_LINES,
|
||||
LARGE_LINES,
|
||||
)
|
||||
|
||||
PASS = 0
|
||||
FAIL = 0
|
||||
|
||||
def test(name):
|
||||
def decorator(fn):
|
||||
global PASS, FAIL
|
||||
try:
|
||||
fn()
|
||||
PASS += 1
|
||||
print(f" [PASS] {name}")
|
||||
except AssertionError as e:
|
||||
FAIL += 1
|
||||
print(f" [FAIL] {name}: {e}")
|
||||
except Exception as e:
|
||||
FAIL += 1
|
||||
print(f" [FAIL] {name}: Unexpected error: {e}")
|
||||
return decorator
|
||||
|
||||
def assert_eq(a, b, msg=""):
|
||||
if a != b:
|
||||
raise AssertionError(f"{msg} expected {b!r}, got {a!r}")
|
||||
|
||||
def assert_true(v, msg=""):
|
||||
if not v:
|
||||
raise AssertionError(msg or "Expected True")
|
||||
|
||||
def assert_false(v, msg=""):
|
||||
if v:
|
||||
raise AssertionError(msg or "Expected False")
|
||||
|
||||
|
||||
print("=== PR Complexity Scorer Tests ===\n")
|
||||
|
||||
print("-- File Classification --")
|
||||
|
||||
@test("dependency file detection — requirements.txt")
|
||||
def _():
|
||||
assert_true(is_dependency_file("requirements.txt"))
|
||||
assert_true(is_dependency_file("src/requirements.txt"))
|
||||
assert_false(is_dependency_file("requirements_test.txt"))
|
||||
|
||||
@test("dependency file detection — pyproject.toml")
|
||||
def _():
|
||||
assert_true(is_dependency_file("pyproject.toml"))
|
||||
assert_false(is_dependency_file("myproject.py"))
|
||||
|
||||
@test("test file detection — pytest style")
|
||||
def _():
|
||||
assert_true(is_test_file("tests/test_api.py"))
|
||||
assert_true(is_test_file("test_module.py"))
|
||||
assert_true(is_test_file("src/module_test.py"))
|
||||
|
||||
@test("test file detection — other frameworks")
|
||||
def _():
|
||||
assert_true(is_test_file("spec/feature_spec.rb"))
|
||||
assert_true(is_test_file("__tests__/component.test.js"))
|
||||
assert_false(is_test_file("testfixtures/helper.py"))
|
||||
|
||||
|
||||
print("\n-- Scoring Logic --")
|
||||
|
||||
@test("small PR gets low score (1-3)")
|
||||
def _():
|
||||
score, minutes, _ = score_pr(
|
||||
files_changed=3,
|
||||
additions=50,
|
||||
deletions=10,
|
||||
has_dependency_changes=False,
|
||||
test_coverage_delta=None
|
||||
)
|
||||
assert_true(1 <= score <= 3, f"Score should be low, got {score}")
|
||||
assert_true(minutes < 20)
|
||||
|
||||
@test("medium PR gets medium score (4-6)")
|
||||
def _():
|
||||
score, minutes, _ = score_pr(
|
||||
files_changed=15,
|
||||
additions=400,
|
||||
deletions=100,
|
||||
has_dependency_changes=False,
|
||||
test_coverage_delta=None
|
||||
)
|
||||
assert_true(4 <= score <= 6, f"Score should be medium, got {score}")
|
||||
assert_true(20 <= minutes <= 45)
|
||||
|
||||
@test("large PR gets high score (7-9)")
|
||||
def _():
|
||||
score, minutes, _ = score_pr(
|
||||
files_changed=60,
|
||||
additions=3000,
|
||||
deletions=1500,
|
||||
has_dependency_changes=True,
|
||||
test_coverage_delta=None
|
||||
)
|
||||
assert_true(7 <= score <= 9, f"Score should be high, got {score}")
|
||||
assert_true(minutes >= 45)
|
||||
|
||||
@test("dependency changes boost score")
|
||||
def _():
|
||||
base_score, _, _ = score_pr(
|
||||
files_changed=10, additions=200, deletions=50,
|
||||
has_dependency_changes=False, test_coverage_delta=None
|
||||
)
|
||||
dep_score, _, _ = score_pr(
|
||||
files_changed=10, additions=200, deletions=50,
|
||||
has_dependency_changes=True, test_coverage_delta=None
|
||||
)
|
||||
assert_true(dep_score > base_score, f"Deps: {base_score} -> {dep_score}")
|
||||
|
||||
@test("adding tests lowers complexity")
|
||||
def _():
|
||||
base_score, _, _ = score_pr(
|
||||
files_changed=8, additions=150, deletions=20,
|
||||
has_dependency_changes=False, test_coverage_delta=None
|
||||
)
|
||||
better_score, _, _ = score_pr(
|
||||
files_changed=8, additions=180, deletions=20,
|
||||
has_dependency_changes=False, test_coverage_delta=3
|
||||
)
|
||||
assert_true(better_score < base_score, f"Tests: {base_score} -> {better_score}")
|
||||
|
||||
@test("removing tests increases complexity")
|
||||
def _():
|
||||
base_score, _, _ = score_pr(
|
||||
files_changed=8, additions=150, deletions=20,
|
||||
has_dependency_changes=False, test_coverage_delta=None
|
||||
)
|
||||
worse_score, _, _ = score_pr(
|
||||
files_changed=8, additions=150, deletions=20,
|
||||
has_dependency_changes=False, test_coverage_delta=-2
|
||||
)
|
||||
assert_true(worse_score > base_score, f"Remove tests: {base_score} -> {worse_score}")
|
||||
|
||||
@test("score bounded 1-10")
|
||||
def _():
|
||||
for files, adds, dels in [(1, 10, 5), (100, 10000, 5000)]:
|
||||
score, _, _ = score_pr(files, adds, dels, False, None)
|
||||
assert_true(1 <= score <= 10, f"Score {score} out of range")
|
||||
|
||||
@test("estimated minutes exist for all scores")
|
||||
def _():
|
||||
for s in range(1, 11):
|
||||
assert_true(s in TIME_PER_POINT, f"Missing time for score {s}")
|
||||
|
||||
|
||||
print(f"\n=== Results: {PASS} passed, {FAIL} failed ===")
|
||||
sys.exit(0 if FAIL == 0 else 1)
|
||||
Reference in New Issue
Block a user