
    `#i-                        d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	 ddl
mZmZmZ ddlmZ ddlmZmZ d	Ze G d
 d             ZdZ G d d      Zy)uG  
core/evolution/code_proposer.py

Story 8.06: CodeProposer — Structural Fix Generator

Uses an LLM (Opus) to generate Python interceptor code for proposed
architectural fixes identified by MetaArchitect.  All external I/O is
dependency-injected so every method is fully testable without real API calls.

Usage::

    proposer = CodeProposer(opus_client=my_llm_fn, axiomatic_tests=AxiomaticTests())
    proposal = proposer.propose(bottleneck, existing_code_context)
    if proposer.validate_proposal(proposal):
        path = proposer.write_proposal(proposal, epoch_id="epoch_001")
    )annotationsN)	dataclassfield)Path)AnyCallableOptional)
Bottleneck)AxiomaticTestsAxiomResulta  You are a Genesis architect. Given this recurring bottleneck:

Description: {description}
Frequency: {frequency}
Affected saga IDs: {saga_ids}
Scar IDs: {scar_ids}

And existing code context:
{existing_code}

Generate a Python interceptor that:
1. Extends BaseInterceptor
2. Addresses the bottleneck
3. Follows Genesis conventions

Return JSON only (no markdown fences):
{{"file_path": "core/interceptors/<name>.py", "code_content": "...", "test_file_path": "tests/interceptors/test_<name>.py", "test_content": "...", "config_changes": {{}}}}
c                  V    e Zd ZU dZded<   ded<   ded<   ded<    ee      Zded	<   y
)CodeProposalu  A proposed code fix generated by CodeProposer.

    Attributes:
        file_path:       E: drive path for the new/modified interceptor file.
        code_content:    Full Python source content of the interceptor.
        test_file_path:  Path for the test file (must start with ``tests/``).
        test_content:    Full Python source content of the test file.
        config_changes:  Any config-layer changes required (key → new_value).
    str	file_pathcode_contenttest_file_pathtest_content)default_factorydictconfig_changesN)__name__
__module____qualname____doc____annotations__r   r   r        5/mnt/e/genesis-system/core/evolution/code_proposer.pyr   r   9   s/     N 6ND6r   r   z./mnt/e/genesis-system/data/evolution/proposalsc                  N    e Zd ZdZ	 	 	 d	 	 	 	 	 	 	 d	dZd
dZddZddZddZy)CodeProposeru8  Generates Python interceptor code from LLM responses for bottleneck fixes.

    All external dependencies are dependency-injected so every code path
    is fully mockable in tests — zero real API or file-system access is
    required by the test suite.

    Args:
        opus_client:     A callable ``(prompt: str) -> str`` that invokes the
                         LLM (typically Opus) and returns raw text.  When
                         ``None`` the proposer raises ``RuntimeError`` on
                         ``propose()`` calls.
        axiomatic_tests: An ``AxiomaticTests`` instance (from story 8.02).
                         Defaults to a fresh ``AxiomaticTests()`` instance.
        proposals_dir:   Base directory for writing proposals.  Defaults to
                         ``data/evolution/proposals``.
    Nc                V    || _         ||n	t               | _        |xs t        | _        y )N)opus_clientr   axiomatic_tests_DEFAULT_PROPOSALS_DIRproposals_dir)selfr"   r#   r%   s       r   __init__zCodeProposer.__init__e   s-     '2A2MSaSc*D.Dr   c                H   | j                   t        d      t        j                  |j                  |j
                  dj                  |j                        xs ddj                  |j                        xs d|      }| j                  |      }| j                  |      }|S )a  Generate a ``CodeProposal`` for the given bottleneck.

        Formats the prompt with bottleneck data + existing code context,
        calls the Opus client, and parses the JSON response into a
        ``CodeProposal`` instance.

        Args:
            bottleneck:            The structural bottleneck to fix.
            existing_code_context: Relevant existing interceptor code to use
                                   as implementation reference in the prompt.

        Returns:
            A ``CodeProposal`` with all five fields populated.

        Raises:
            RuntimeError: If ``opus_client`` is not set.
            ValueError:   If the LLM response is not valid JSON or is missing
                          required fields.
        zXCodeProposer.propose() requires an opus_client callable.  Pass one at construction time.z, none)description	frequencysaga_idsscar_idsexisting_code)
r"   RuntimeErrorPROPOSE_PROMPTformatr*   r+   joinaffected_saga_idsr-   _parse_response)r&   
bottleneckexisting_code_contextpromptraw_responseproposals         r   proposezCodeProposer.proposes   s    ( #1 
  &&".. **YYz;;<FYYz223=v/ ' 
 !,,V4''5r   c                    | j                   j                  |j                  i       }|j                  syd|j                  vry|j                  j                  d      syy)u1  Validate a ``CodeProposal`` against Genesis axioms.

        Runs ``AxiomaticTests.run_all`` on the proposal's ``code_content``.
        A proposal is valid only when:
          • No axiom violations are found in ``code_content``, AND
          • ``code_content`` contains the string ``"BaseInterceptor"``
            (confirming the generated class inherits from the correct base).

        Args:
            proposal: The ``CodeProposal`` to validate.

        Returns:
            ``True`` iff all axioms pass and ``BaseInterceptor`` is present.
        )r   state_contentFBaseInterceptorztests/T)r#   run_allr   passedr   
startswith)r&   r9   results      r   validate_proposalzCodeProposer.validate_proposal   sf      #22::!.. ; 
 }} H$9$99 &&11(;r   c                   t        | j                        |z  }||j                  z  }|j                  j	                  dd       |j                  |j                  d       ||j                  z  }|j                  j	                  dd       |j                  |j                  d       ||j                  |j                  |j                  d}|dz  }|j                  t        j                  |d      d       t        |      S )	u  Write a ``CodeProposal`` to disk under ``proposals_dir/{epoch_id}/``.

        Creates the directory structure if it does not exist.  Writes:
          • The interceptor source to ``{proposals_dir}/{epoch_id}/{file_path}``
          • The test source to ``{proposals_dir}/{epoch_id}/{test_file_path}``
          • A ``proposal.json`` metadata file in ``{proposals_dir}/{epoch_id}/``

        Args:
            proposal:  The ``CodeProposal`` to persist.
            epoch_id:  The evolution epoch identifier (e.g. ``"epoch_001"``).

        Returns:
            The absolute path to the ``proposal.json`` metadata file as a string.
        T)parentsexist_okzutf-8)encoding)epoch_idr   r   r   zproposal.json   )indent)r   r%   r   parentmkdir
write_textr   r   r   r   jsondumpsr   )r&   r9   rG   baseinterceptor_path	test_pathmetadatamanifest_paths           r   write_proposalzCodeProposer.write_proposal   s     D&&'(2  ("4"44%%dT%B##H$9$9G#L 8222	td;X22WE !!++&55&55	
 .  HQ!?' R=!!r   c                    |j                         }|j                  d      rQ|j                         }|dd }|r|d   j                         dk(  r|dd }dj                  |      j                         }	 t	        j
                  |      }t        |t              s't        d	t        |      j                  d
|dd       |}d}|D cg c]	  }||vs| }	}|	r(t        d|	 dt        |j                                      t        t        |d         t        |d         t        |d         t        |d         t        |j!                  di                   S # t        j                  $ r}t        d| d|dd       |d}~ww xY wc c}w )a  Parse the LLM response string into a ``CodeProposal``.

        Strips markdown code fences (``` ... ```) if present, then
        parses the JSON body.

        Args:
            raw: Raw text response from the Opus client.

        Returns:
            A populated ``CodeProposal`` instance.

        Raises:
            ValueError: If the JSON cannot be decoded or required fields
                        (``file_path``, ``code_content``, ``test_file_path``,
                        ``test_content``) are missing.
        z```   N
z;CodeProposer: LLM response is not valid JSON. Parse error: z". Raw response (first 200 chars):    z%CodeProposer: LLM response parsed to zB, expected a JSON object (dict).  Raw response (first 200 chars): )r   r   r   r   z<CodeProposer: LLM response JSON is missing required fields: z. Keys present: r   r   r   r   r   )r   r   r   r   r   )stripr@   
splitlinesr2   rM   loadsJSONDecodeError
ValueError
isinstancer   typer   listkeysr   r   get)
r&   rawcleanedlinesparsedexcdatarequiredfmissings
             r   r4   zCodeProposer._parse_response   s   " ))+ e$&&(E!"IEr*e3cr
ii&,,.G	**W-F &$'7V8M8M7P QSSVW[X[S\R_a 
  & S&8!4-188Nwi X!!%diik!2 35 
 ${+,T.12t$456T.12)92 >?
 	
/ ## 	  #u$Fs4CymU 	  9s$   4E 	FFF.FF)NNN)r"   zOptional[Callable[[str], str]]r#   zOptional[AxiomaticTests]r%   zOptional[str]returnNone)r5   r
   r6   r   rm   r   )r9   r   rm   bool)r9   r   rG   r   rm   r   )rd   r   rm   r   )	r   r   r   r   r'   r:   rB   rT   r4   r   r   r   r    r    S   sY    & 7;48'+	E3E 2E %	E
 
E$LB%"V;
r   r    )r   
__future__r   rM   osdataclassesr   r   pathlibr   typingr   r   r	   core.evolution.meta_architectr
   core.evolution.axiomatic_testsr   r   r0   r   r$   r    r   r   r   <module>rw      s\   " #  	 (  * * 4 F0 7 7 7, J M
 M
r   