feat(fase2): wire Step-1 ExpeL retrieval into the hypothesis prompt
Closes maalbilde §5 gap #1 (the one missing "feedback-into-prompt" dataflow) for the OKF-bundle path. Before, ExpeL was computed AFTER generation into a discarded SessionContext, so a prior verdict could not influence any hypothesis (context_providers=0). - New okf.py: framework-neutral OKF bundle navigation (index + frontmatter + cross-links), pure stdlib, no agent_framework/mcp (D7-portable), enforced by test_okf_is_maf_free. - verdicts.py: seed_store_from_bundle + bundle_candidate_features build the ExpeL substrate + the pre-hypothesis query key from a bundle. - run_project(bundle_dir=...): folds the candidate's prior verdicts into the generation context BEFORE generate_via_llm; the road path is unchanged. Load-bearing (maalbilde §7): test_step1_expel_loadbearing proves a prior verdict reaches the hypothesis prompt and goes RED when the fold is detached (shown via TDD red->green). The marker is the minted verdict id (content hash) because docs_dir==bundle_dir lets keyword chunk-stuffing leak the realization rate; clean layer separation is Fase 2b. Suite 121->133 passed; mypy + ruff check clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHR8iKxJRxDiDfNw8HZmWE
This commit is contained in:
parent
2b1a1832ef
commit
d6d83d42b5
9 changed files with 529 additions and 7 deletions
|
|
@ -163,6 +163,43 @@ def make_portfolio_client_factory() -> Callable[..., Callable[[str], BaseChatCli
|
|||
return _make
|
||||
|
||||
|
||||
class _RecordingChatClient(SyntheticUsageChatClient):
|
||||
"""Records the incoming prompt blob per call into a SHARED sink, then returns a fixed valid
|
||||
reply. Lets a test assert exactly what text reached the prompt — the probe the Step-1 ExpeL
|
||||
wiring is made load-bearing against (does a prior verdict reach the hypothesis prompt?)."""
|
||||
|
||||
def __init__(self, sink: list[str], reply: str, *, tokens_per_reply: int = 8) -> None:
|
||||
super().__init__(default_reply=reply, tokens_per_reply=tokens_per_reply)
|
||||
self._sink = sink
|
||||
|
||||
def _inner_get_response(
|
||||
self, *, messages: Sequence[Message], stream: bool, options: Any, **kwargs: Any
|
||||
) -> Any:
|
||||
self._sink.append(" ".join(getattr(m, "text", "") or "" for m in messages))
|
||||
return super()._inner_get_response(
|
||||
messages=messages, stream=stream, options=options, **kwargs
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def make_recording_client_factory() -> Callable[
|
||||
[str], tuple[Callable[[str], BaseChatClient], list[str]]
|
||||
]:
|
||||
"""Return a maker that builds a per-role client factory recording every prompt blob into a
|
||||
shared list. Returns ``(factory, recorded_prompts)`` so the test inspects what reached the
|
||||
prompt across the whole run (debate rounds + generation)."""
|
||||
|
||||
def _make(reply: str) -> tuple[Callable[[str], BaseChatClient], list[str]]:
|
||||
sink: list[str] = []
|
||||
|
||||
def factory(role: str) -> BaseChatClient:
|
||||
return _RecordingChatClient(sink, reply)
|
||||
|
||||
return factory, sink
|
||||
|
||||
return _make
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def fresh_store() -> VerdictStore:
|
||||
return VerdictStore(verdicts=[])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue