
    +aj?                       d Z ddlmZ ddlZddlZddlZddlmZmZ ddlm	Z	  e	d      Z
 e	d      Z e	d      Z e	d	      Zd
ZddZddZddZddZddZddZddZd dZddZddZddZddZd!dZedk(  r e        yy)"a"  Explicit, append-only group review and safe backlog migration CLI.

Default operation is read-only.  Status changes require --apply and are
limited to deterministic non-knowledge or insufficient-evidence classes.
Group approvals are recorded separately and never write truth automatically.
    )annotationsN)datetimetimezone)Pathz?/opt/struktur/obsidian-knowledge-pipeline/knowledge_pipeline.dbz//opt/struktur/knowledge-curator/review_pilot.dbz7/opt/struktur/knowledge-curator/minimal-human-review.dbzA/opt/struktur/knowledge-curator/minimal-human-review-dry-run.jsonzminimal-human-review/1.0.0c                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     5/opt/struktur/knowledge-curator/minimal_review_cli.pyr	   r	      s    <<%//11r   c                 b    t        j                  t              } t         j                  | _        | S r   )sqlite3connectMHRRowrow_factorycs    r   dbr      s    Agkkam!8r   c                F    | j                  d       | j                          y )Naw  
      CREATE TABLE IF NOT EXISTS group_decisions(
        group_decision_id TEXT PRIMARY KEY, cluster_id TEXT NOT NULL, decision TEXT NOT NULL,
        reviewer TEXT NOT NULL, reason TEXT NOT NULL, scope TEXT NOT NULL, applied INTEGER NOT NULL DEFAULT 0,
        reversible INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS group_decision_members(
        group_decision_id TEXT NOT NULL, candidate_id TEXT NOT NULL, PRIMARY KEY(group_decision_id,candidate_id)
      );
      CREATE TABLE IF NOT EXISTS rule_proposals(
        rule_id TEXT PRIMARY KEY, version TEXT NOT NULL, pattern_json TEXT NOT NULL, status TEXT NOT NULL,
        creator TEXT NOT NULL, approval_basis TEXT, approved_by TEXT, approved_at TEXT,
        test_population INTEGER, sample_results_json TEXT, exclusions_json TEXT, sensitivity_limit TEXT,
        rollback_reference TEXT, created_at TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS rule_audit(
        audit_id TEXT PRIMARY KEY, rule_id TEXT NOT NULL, action TEXT NOT NULL, actor TEXT NOT NULL,
        details_json TEXT, created_at TEXT NOT NULL
      );
    )executescriptcommitr   s    r   ensure_schemar      s    OO  	& ((*r   c                d    | j                  d      j                         }|st        d      |d   S )NzCSELECT run_id FROM analysis_runs ORDER BY completed_at DESC LIMIT 1uF   Keine Analyse vorhanden; zuerst minimal_review_analysis.py ausführen.run_id)executefetchone
SystemExit)r   rows     r   
latest_runr"   6   s2    
))Y
Z
c
c
eCj!ijjx=r   c                V   | j                  d|f      j                         }|st        d|       | j                  d|f      j                         }t	        t        j                  t        |      |d d D cg c]  }t        |       c}t        |      dddd	             y c c}w )
N)SELECT * FROM clusters WHERE cluster_id=?Unbekannter Cluster: zSELECT ca.* FROM candidate_analysis ca JOIN cluster_members cm ON cm.candidate_id=ca.candidate_id WHERE cm.cluster_id=? ORDER BY ca.path,ca.candidate_idd   T)clustermembersmember_count requires_explicit_human_decisionF   ensure_asciiindent)	r   r   r    fetchallprintjsondumpsdictlen)r   
cluster_idr!   r(   ms        r   show_clusterr7   <   s    
))?
N
W
W
YCj#8!EFFII  q  s}  r  @  I  I  KG	$**S	gdsm4TT!W4Tdghodp  UY  Z  hm  uv  w  x4Ts   8B&c           
         | j                  d|f      j                         }t        t        j                  |D cg c]  }t        |       c}dd             y c c}w )NzvSELECT cluster_id,candidate_count,document_count,status FROM clusters ORDER BY candidate_count DESC,cluster_id LIMIT ?Fr+   r,   )r   r/   r0   r1   r2   r3   )r   limitrowsxs       r   list_clustersr<   C   s^    	
  L  NS  MU  
V  
_  
_  
aD	$**t,!d1g,%q
IJ,s   Ac                   t        j                  t        j                  d            }|d   D cg c]  }|d   dv s| }}| j                  r|d | j                   }| j
                  sRt        t        j                  dt        |      dD ci c]  t        fd|D               c}ddd	d
             y t        j                  t              }t        j                  t              }t               }t        |       dt!        j"                  t$        j&                        j)                  d      z   }d}	d}
d}	 |j+                  d      D ch c]  }|d   	 }}dD ]!  \  }}||vs|j+                  d| d|        # |j-                          t/        dt        |      d      D ]  }|||dz    }t#               }|dz  }|||fD ]  }|j+                  d        	 |dz   t1        |      z   }|j+                  d|t2        d||t        |      ddf       |D ]!  }|j+                  d|d   f      j5                         }|r|d   dk7  r3|d   dk(  rdnd}|dk(  rd nd!}d"|d#   z   }|j+                  d$|||t2        |||d   f       |j+                  d%|d&|d   d'd|t        j                  |dd(d	)      |f       |j+                  d*|d   f      j5                         }|r|
dz  }
nLd+| d,}|j+                  d-d.|d   z   |d   d||d |d/f       |j+                  d0d1|d   z   |d   d|d2d||f       |	dz  }	$ |j-                          |j-                          |j-                           t        t        j                  |d	|	|
t        |      |dddd3	d	d
             |j;                          |j;                          |j;                          y c c}w c c}w c c}w # t6        $ r2 |j9                          |j9                          |j9                           w xY w# |j;                          |j;                          |j;                          w xY w)4Nzutf-8)encodingitemscategory)excluded_non_knowledgeinvalid_fragmentTc              3  .   K   | ]  }|d    k(    yw)r@   Nr   ).0r;   ks     r   	<genexpr>zsafe_migrate.<locals>.<genexpr>M   s     UyklVWXbVcefVfUys   )dry_runselected
categoriesno_status_changesFr+   r,   zmhr-safe-migration-%Y%m%dT%H%M%S%fZr   zPRAGMA table_info(review_items)   ))classification_statusTEXT)canonical_candidate_idrN   )classification_rulerN   )classification_versionrN   )classified_atrN   )duplicate_ofrN   z$ALTER TABLE review_items ADD COLUMN  i  BEGIN IMMEDIATE-zINSERT OR IGNORE INTO review_runs(run_id,curator_version,normalization_version,started_at,completed_at,candidate_count,reviewer,status) VALUES(?,?,?,?,?,?,?,?)z
kc-norm-v4zpipeline-optimizer	completed4SELECT status FROM review_items WHERE candidate_id=?candidate_idpending_reviewrA   rB   rezminimal-review.classification_reasonUPDATE review_items SET status=?,classification_status=?,classification_rule=?,classification_version=?,classified_at=?,updated_at=? WHERE candidate_id=?|INSERT INTO audit(run_id,entity_type,entity_key,phase,old_status,new_status,details_json,created_at) VALUES(?,?,?,?,?,?,?,?)review"minimal_review_safe_classification)ruleno_truth_writer-   ;SELECT 1 FROM review_decisions WHERE candidate_id=? LIMIT 1z,Deterministic minimal-review classification z; no truth decision.INSERT INTO review_decisions(decision_id,candidate_id,reviewer,decision,reason,corrected_candidate_json,reviewed_at,review_duration_seconds) VALUES(?,?,?,?,?,?,?,?)z	auto-mhr-        INSERT INTO review_audit(audit_id,candidate_id,previous_state,new_state,action,actor,reason,created_at) VALUES(?,?,?,?,?,?,?,?)zauto-mhr-audit-minimal_safe_classification)	migration_run_idrG   changedprotected_existing_decisionsrH   batchesbatch_limitrc   no_neo4j_write)r1   loadsANALYSIS	read_textr9   applyr0   r2   r4   sumr   r   PIPEREVIEWr   r   r   r	   r   r
   strftimer   r   rangestrVERSIONr   	Exceptionrollbackclose)argsanalysisr;   rH   rE   pcrcmcr   rk   	protectedrm   r[   colsnametypstartbatchtconn	batch_runitemoldnewdecisionrb   priorreasons       `                       r   safe_migrater   H   sf   ZZ**G*<=H!'*mAa
m?l.lamHmzzH[djj18::djjDCM  Dq  Pr~PQRUUypxUyRyPy  Pr  GK  L  Z_  gh  i  	j  lrtB!8bRT"=QSCT]rs{ss  AI  AM  AM  tN  tW  tW  Xj  tk  ^kV\GyAG0JJ'HIJqadJJ O 	aHD4.RSWRXXYZ]Y^,_!`	a
		1S]3/ 	CE5s+EsuQgqjgBr
CDLL1B$CCC *S\1	

  }  H  IP  Q]  ^_  `a  be  fk  bl  mA  BM  ~N  O! D

#Y[_`n[oZqr{{}C#a&*:":H484DF^4^0dvC  IL  Nf  If  BE  lo  yA  vG  HL  Md  He  ve  quJJ   {  }@  AD  EI  JQ  RS  TU  VZ  [i  Vj  |k  lJJ   ^  `i  jr  sw  xF  sG  Hl  m}  ~A  BF  BL  BL  UY  ko  Mp  ~C  BD  EF  _G  H**%bdhiwdxcz{  E  E  GEili!MdVSgh

  $J  LW  X\  ]k  Xl  Ll  mq  r@  mA  BV  W_  `f  gk  lm  nq  Kr  s

  $e  gx  y}  ~L  yM  gM  NR  Sa  Nb  cs  tw  xU  Vj  kq  rs  ft  uQJG 		RYY["))+'	C, 	djjVegu~  KN  OW  KX  cj  y|  NR  dh  i  w|  DE  F  	GXXZRXXZE n Pr K0  Cr{{}bkkmUC XXZRXXZsN   OOO!P) O& P) /A5P) %FO+47P) &P) +;P&&P) )2Qc                   |dvryddddd}||   \  }}t        j                  t              }t        j                  t              }	t	               }
d}||	fD ]  }|j                  d        	 | D ]  }|j                  d	|f      j                         }|r|d   d
k7  r/|dk7  rS|j                  d||d|z   t        |
|
|f       |j                  d|d|dd
|t        j                  ||ddd      |
f       |	j                  d|f      j                         }|sF|	j                  dd|z   dz   |z   ||||d |
df       |	j                  dd|z   dz   |z   |d
|d|||
f       |dz  } |j                          |	j                          ||j                          |	j                          S # t        $ r" |j                          |	j                           w xY w# |j                          |	j                          w xY w)N>   reject_patternapprove_patternmark_non_knowledge_pattern"mark_insufficient_evidence_patternr   )approved_for_pilotr   )rejectedr[   )rA   r[   )rB   r\   )r   r   r   r   rU   rX   rZ   r   r^   zgroup_decision:r_   r`   group_decisionT)r   group_idrc   Frd   re   rf   zgroup-rV   rg   rh   zgroup-audit-human_group_decisionrL   )r   r   ru   rv   r	   r   r   rz   r1   r2   r   r}   r{   r|   )
candidatesr   r   reviewerr   mappedpipeline_statusreview_coder   r   r   rk   r   cidr   r   s                   r   apply_group_membersr   o   s(   }}J]m  Mk  Rj  kF#)(#3 O[tB!8bCE!17B8&788$ 
	C

QSVRXYbbdC#a&$44h,,

  w  yH  IX  Yj  ks  Ys  t{  |}  ~  @C  xD  E

  Z  \d  em  nq  rB  CS  Tc  dh  dn  dn  {C  OW  im  on  |A  dB  CD  [E  F**Z\_[abkkmE

  B  DL  MU  DU  VY  DY  Z]  D]  ^a  bj  kv  w}  ~B  CD  EH  CI  J

  ]  _m  nv  _v  wz  _z  {~  _~  B  CS  Tc  dz  {C  DJ  KL  ^M  NQJG
	 			RYY[ XXZ  ,
r{{}e,XXZs   .DF) )+GG "G9c                   t               }t        |       |j                  d| j                  f      j	                         }|st        d| j                         | j                  dvrt        d      |j                  d| j                  f      D cg c]  }|d   	 }}dt        j                  t        j                        j                  d      z   }|j                  d	|| j                  | j                  | j                  | j                  | j                  d
dt               f	       |j                  d|D cg c]  }||f c}       |j!                          |j#                          | j$                  r-t'        || j                  | j                  | j                  |      nd
}| j$                  r=t               }|j                  d|f       |j!                          |j#                          t)        t+        j,                  || j                  | j                  t/        |      t1        | j$                        |dddd             y c c}w c c}w )Nr$   r%   >   split_clusterr   r   approve_only_selectedrequire_individual_reviewr   r   u   Ungültige Gruppenentscheidung;SELECT candidate_id FROM cluster_members WHERE cluster_id=?rY   zgroup-decision-rK   z5INSERT INTO group_decisions VALUES(?,?,?,?,?,?,?,?,?)r   rL   z.INSERT INTO group_decision_members VALUES(?,?)z>UPDATE group_decisions SET applied=1 WHERE group_decision_id=?zExplicit group decision recorded append-only. No automatic future scope; current/superseded/verified is never assigned by this command.)group_decision_idr5   r   candidate_countappliedrk   noteFr+   r,   )r   r   r   r5   r   r    r   r   r	   r   r
   rw   r   r   scopeexecutemanyr   r}   rs   r   r0   r1   r2   r4   bool)r~   r   r!   r[   r(   gidr6   rk   s           r   record_groupr      sy   dAM!!)),WY]YhYhXj"k"t"t"vcj#88I!JKK}}  G  G  OY  Zz  O{  I{()		2oqu  rA  rA  qC  )D  E1Q~  EG  E(,,x||4==>PQQCIIEs4??[_[h[himivivw{  xC  xC  DH  DN  DN  OP  QR  SV  SX  GY  ZMMBU\C]PQSGC]^`a`h`h`jGGIX\XbXbdkk$--PSThiGzz
$		Z\_[abdedldldnpqpwpwpy	$**#4??VZVcVcvy  {B  wC  NR  SW  S]  S]  N^  ip  xA  B  PU  ]^  _  ` E D^s   I;I
c                   t               }t        |       |j                  d| j                  f      j	                         }|st        d| j                         |j                  d| j                  f      D cg c]  }|d   	 }}dt        j                  t        j                        j                  d      z   }| j                  |d   |d   |d	d
d}|j                  d|dt        j                  |d
      d| j                  | j                  d d t        |      d t        j                  ddid
      dd t               f       |j                  dd|z   |d| j                  t        j                  |d
      t               f       |j!                          |j#                          t%        t        j                  |dt        |      dd
dd
d             y c c}w )Nr$   r%   r   rY   z	mhr-rule-rK   cluster_keyr   existing_onlyF)r5   r   r   candidate_idsr   automatic_truthz>INSERT INTO rule_proposals VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)zmhr-rule-v1rd   	suggestedsensitive_or_conflictTnever_without_explicit_review*INSERT INTO rule_audit VALUES(?,?,?,?,?,?)rule-audit-suggest_rule)rule_idstatusr   requires_explicit_approvalr   r+   r,   )r   r   r   r5   r   r    r   r	   r   r
   rw   r1   r2   creatorbasisr4   r   r}   r0   )r~   r   r!   r[   r(   r   patterns          r   r   r      s   dAM!!)),WY]YhYhXj"k"t"t"vcj#88I!JKK()		2oqu  rA  rA  qC  )D  E1Q~  EG  EX\\2;;<NOOG//M8J]`ar]s  EL  Ud  w|  }GIINPWXefjfpfpqx  GL  gM  NY  Z^  Zf  Zf  gk  gq  gq  rv  w{  |  @G  |H  IM  NR  NX  NX  Zq  rv  Yw  EJ  NK  Lk  lp  qt  qv  Pw  xII:M'<QRYZhimiuiuvz  wA  wA  BI  W\  w]  ^a  ^c  <d  e  gh  go  go  gq  st  sz  sz  s|	$**sSZ|y}  QV  W  ej  rs  t  u Es   :Gc                4   t               }t        |       |j                  d| j                  f      j	                         }|st        d| j                         ddddd}| j                  |vrt        d      || j                     }t               }|j                  d	|| j                  || j                  | j                  f       |j                  d
d| j                  z   dz   | j                  z   | j                  | j                  | j                  t        j                  | j                  ddd      |f       |j                          |j                          t        t        j                  | j                  | j                  |ddddd             y )N,SELECT * FROM rule_proposals WHERE rule_id=?Unbekannte Regel: activer   disabledrolled_backapprove_rulereject_ruledisable_rulerollback_ruleu   Ungültige Regelaktionz_UPDATE rule_proposals SET status=?,approved_by=?,approved_at=?,approval_basis=? WHERE rule_id=?r   r   rV   F)r   r   rd   zgRule status is audited; pipeline application remains disabled until separately integrated and approved.)r   actionr   automatic_future_applicationr   r+   r,   )r   r   r   r   r   r    r   r	   actorr   r1   r2   r   r}   r0   )r~   r   r!   mappingr   r   s         r   rule_actionr      s   dAM!!)),Z\`\h\h[j"k"t"t"vcj#5dll^!DEE$:ZhuvG{{'!4L)M#M4;;F35!))  5V  X^  _c  _i  _i  jk  lp  lw  lw  x|  xD  xD  WE  +FII:M$++<UVY<YZ^ZfZf<fgkgsgstxtt  AE  AK  AK  LP  LV  LV  ae  al  al  D  WE  SX  LY  Z[  <\  ]  _`  _g  _g  _i  kl  kr  kr  kt	$**dkk6qv  h  i  w|  DE  F  Gr   c                   t               }|j                  d| j                  f      j                         }|st	        d| j                         t        j                  |d         }|j                  dg       }|j                          t        t        j                  | j                  |d   t        |      t        dt        |            dddd	d
             y )Nr   r   pattern_jsonr   r   r&   T)r   r   test_populationsample_sizefalse_positive_review_requiredrJ   Fr+   r,   )r   r   r   r   r    r1   rp   getr}   r0   r2   r4   min)r~   r   r!   r   idss        r   	test_ruler      s    dA		H$,,YbbdCj#5dll^!DEEJJs>*+G_R1PSRSRYRYR[	$**c(mVYZ]V^mpqtuxy|u}m~  ae  z~    MR  Z[  \  ]r   c                    t        j                         } | j                  dd      }|j                  d      }|j	                  dt
        d       |j                  d      }|j	                  d	       |j                  d
      }|j	                  dd       |j	                  dt
        d       |j                  d      }|j	                  d	       |j	                  dd       |j	                  dd       |j	                  dd       |j	                  dddgd       |j	                  dd       |j                  d      }|j	                  d	       |j	                  dd       |j	                  dd       |j                  d      }|j	                  d       |j	                  dg dd       |j	                  d d       |j	                  dd       |j                  d!      }|j	                  d       | j                         }t               }t        |       |j                  dk(  r't        ||j                         |j                          y |j                  dk(  r't        ||j                         |j                          y |j                  d
k(  r|j                          t        |       y |j                  dk(  r|j                          t!        |       y |j                  dk(  r|j                          t#        |       y |j                  dk(  r|j                          t%        |       y |j                  d!k(  r|j                          t'        |       y y )"NcmdT)destrequiredzlist-clustersz--limit2   )typedefaultzshow-clusterr5   zmigrate-safez--apply
store_true)r   r   zrecord-groupz
--decision)r   z
--reviewerz--reasonz--scopeapply_to_existing_onlyapply_to_existing_and_future)choicesr   zsuggest-rulez	--creatorz--basiszrule-actionr   z--actionr   )r   r   z--actorz	test-rule)argparseArgumentParseradd_subparsers
add_parseradd_argumentint
parse_argsr   r   r   r<   r9   r}   r7   r5   r   r   r   r   r   )apsubsr~   r   s        r   mainr      sn    Bb&7&7UD&7&Q#	nn_%Aq~~iSQS~'T	nn^$Aann\&B	nn^$AannYln&SUVUcUcdmsv  @AUc  VB	nn^$Aann\&BANNS_imNDnpqp~p~  @L  VZp~  q[  ]^  ]k  ]k  lv  @D  ]k  ]E  GH  GU  GU  V_  iA  B`  ha  jB  GU  GC  EF  ES  ES  T]  eq  ES  Er	nn^$Aann\&BANNS^hlNDmopo}o}  H  RVo}  pW	nn]#AQ^^I%>z  c`  jn  Ao  qr  q  q  @I  SW  q  qX  Z[  Zh  Zh  is  }A  Zh  ZB	nn[!A1>>)#<	DBD!-"2xx -$**"=qwwy	>	!<$//#BAGGI	>	!1779l4.@	>	!1779l4.@	>	!1779l4.@	=	 !'')[->	;		9T?	r   __main__)returnry   )r   sqlite3.Connection)r   r   r   None)r   r   r   ry   )r   r   r5   ry   r   r   )r   r   r9   r   r   r   )r~   zargparse.Namespacer   r   )r   z	list[str]r   ry   r   ry   r   ry   r   ry   r   r   )r   r   )__doc__
__future__r   r   r1   r   r   r   pathlibr   ru   rv   r   rq   rz   r	   r   r   r"   r7   r<   r   r   r   r   r   r   r   __name__r   r   r   <module>r      s    #    ' MN	?	@
DEST
&2D.xK
$0N$2`uG];& Zr   