Critic gate signals, ported from the agent middleware (CORE-403).
:func:compute_confidence and :func:detect_sensitive_hit are verbatim
ports of symfonic.agent.middleware.confidence -- pure functions, copied
rather than imported so this package depends on nothing outside itself and
the kernel. :func:critic_gate_fires ports
MetacognitiveMiddleware._gate_fires: fire on low confidence OR a
sensitive hit. The selective smart gate, adopter gates, and
fabrication/intent threading stay on the middleware; the port carries the
core contract only.
Per-turn signals arrive through the resolved snapshot under
:data:SIGNALS_ENTRY. No kernel stage publishes activation records yet; a
composition root that wants firing behavior supplies the entry (tests supply
fixtures). Absent, the critic degrades to cold-start neutral -- confidence
0.5, no hit -- so with the stock 0.6 threshold it reviews every turn rather
than silently approving ungrounded ones.
activation_from_resolved
activation_from_resolved(resolved: Any) -> dict[str, Any]
The turn's activation record, or {} when no producer supplied one.
Source code in src/symfonic/capabilities/critic/signals.py
| def activation_from_resolved(resolved: Any) -> dict[str, Any]:
"""The turn's activation record, or ``{}`` when no producer supplied one."""
if resolved is None:
return {}
if isinstance(resolved, Mapping):
entry = resolved.get(SIGNALS_ENTRY)
else:
get = getattr(resolved, "get", None)
entry = get(SIGNALS_ENTRY) if callable(get) else None
if entry is None:
return {}
value = getattr(entry, "value", entry)
return dict(value) if isinstance(value, Mapping) else {}
|
compute_confidence
compute_confidence(activation_log: dict[str, Any]) -> float
Deterministic heuristic in [0.0, 1.0]. Returns 0.5 on empty input.
Formula
confidence = clamp01(
0.4 * seed_strength
+ 0.3 * path_coverage
+ 0.3 * edge_weight_factor
)
Where
seed_strength = avg score of activated_nodes (>= 1.0 if seeded)
path_coverage = min(1.0, len(inference_paths) / 3)
edge_weight_factor = avg edge weight of traversed_edges / 5.0, capped at 1.0
Source code in src/symfonic/capabilities/critic/signals.py
| def compute_confidence(activation_log: dict[str, Any]) -> float:
"""Deterministic heuristic in [0.0, 1.0]. Returns 0.5 on empty input.
Formula:
confidence = clamp01(
0.4 * seed_strength
+ 0.3 * path_coverage
+ 0.3 * edge_weight_factor
)
Where:
seed_strength = avg score of activated_nodes (>= 1.0 if seeded)
path_coverage = min(1.0, len(inference_paths) / 3)
edge_weight_factor = avg edge weight of traversed_edges / 5.0, capped at 1.0
"""
if not activation_log:
return 0.5
activated_nodes = activation_log.get("activated_nodes") or []
inference_paths = activation_log.get("inference_paths") or []
traversed_edges = activation_log.get("traversed_edges") or []
# seed_strength: average score of activated_nodes
if activated_nodes:
scores = [n.get("score", 0.0) for n in activated_nodes]
seed_strength = sum(scores) / len(scores)
else:
return 0.5 # cold start — neutral
# path_coverage
path_coverage = min(1.0, len(inference_paths) / 3.0)
# edge_weight_factor
if traversed_edges:
weights = [e.get("weight", 0.0) for e in traversed_edges]
edge_weight_factor = min(1.0, (sum(weights) / len(weights)) / 5.0)
else:
edge_weight_factor = 0.0
confidence = (
0.4 * seed_strength
+ 0.3 * path_coverage
+ 0.3 * edge_weight_factor
)
return _clamp01(confidence)
|
critic_gate_fires
critic_gate_fires(confidence: float, sensitive_hit: str | None, threshold: float) -> bool
Fire on low confidence OR a sensitive hit (legacy gate, verbatim).
Source code in src/symfonic/capabilities/critic/signals.py
| def critic_gate_fires(
confidence: float, sensitive_hit: str | None, threshold: float
) -> bool:
"""Fire on low confidence OR a sensitive hit (legacy gate, verbatim)."""
if confidence < threshold:
return True
return sensitive_hit is not None
|
detect_sensitive_hit
detect_sensitive_hit(activation_log: dict[str, Any], sensitive_tags: frozenset[str]) -> str | None
Return the first matching tag when any activated_node carries one of
sensitive_tags in properties['tags'], else None.
Source code in src/symfonic/capabilities/critic/signals.py
| def detect_sensitive_hit(
activation_log: dict[str, Any],
sensitive_tags: frozenset[str],
) -> str | None:
"""Return the first matching tag when any activated_node carries one of
*sensitive_tags* in properties['tags'], else None."""
if not sensitive_tags:
return None
activated_nodes = (activation_log or {}).get("activated_nodes") or []
for node in activated_nodes:
node_tags = node.get("tags") or node.get("properties", {}).get("tags") or []
for tag in node_tags:
if tag in sensitive_tags:
return str(tag)
return None
|