Separate “what the tool said” from “what it proves.” ObserverAgent turns raw tool output into structured Observation facts/signals/findings; OutcomeJudge + HypothesisRepository judge that structured evidence against the task’s success_criteria/stop_conditions, persist hypothesis state and OutcomeAssessment, and reject terminal or duplicate checks. A succeeded tool run is not proof until the judge says so.
Provide Observation dataclass (observer.py:26): task_id/target/tool_name/input_summary/output_summary/facts/new_{assets,endpoints,parameters,technologies,identities,objects}/interesting_signals/possible_findings/dead_ends/recommended_followup_tasks/memory_updates/graph_updates/evidence_refs/hypothesis_evidence/confidence/usefulness.
Dispatch observe(task, raw_output, tool_name, prior_state?, evidence_refs?) (observer.py:102) by tool family:
nmap/scan → port/proto/state/service, OS line (observer.py:173_parse_nmap_output)
Optionally embed the summarized observation via SemanticMemoryManager.store_embedding and set confidence via tools/intelligence/adapters/observer_adapter.ObserverAdapter().infer_confidence when refs exist.
Structured facts/new_endpoints/technologies/hypothesis_evidence/references, never raw words like “success” (filtered via _STOPWORDS, outcome_judge.py:925)
evidence_refs
Union of all three sources via _merge_refs
Output
Notes
Observation
Prerequisite for judge; persisted via agent_loop._save_observation
flowchart TD
A[ExecutionResult + Observation] --> B[_execution_outcome: success? no+blockedMarkers? -> failed/blocked]
B --> C[criteria: _criterion_met each success_criteria / stop_conditions]
C --> D[_explicit_evidence_scores from hypothesis_evidence polars]
D --> E[criteria_ratio vs explicit scores => support/refutation]
E --> F{has_terminal_evidence && score>=threshold?}
F -->|refute| G[hypothesis_status=refuted]
F -->|confirm| H[hypothesis_status=confirmed]
F -->|no| I{attempted && independent_count>=max?}
I -->|yes| J[exhausted]
I -->|no| K[inconclusive/open + another_justified flag]
G & H & J & K --> L[_information_value blend -> OutcomeAssessment]
Criterion matching (_criterion_met) supports both structured {field, operator, value} (operators: exists/count_gte/gte/equals/not_equals/contains/not_contains) and free-text clauses (token-overlap heuristic with stopword filter).