-
Notifications
You must be signed in to change notification settings - Fork 0
[codex] Pin agent-core v0.7 insight contract #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,239 @@ | ||
| """Private insight ledger for Engineering Loop proactivity decisions. | ||
|
|
||
| The public ``agent-core`` contract owns the canonical shape. This module emits | ||
| compatible JSON and validates it when records are written. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import hashlib | ||
| import importlib | ||
| import json | ||
| from datetime import UTC, datetime | ||
| from pathlib import Path | ||
| from typing import Any, Literal | ||
|
|
||
| InsightAction = Literal["notify", "question", "draft", "stay_silent"] | ||
| SamplingClass = Literal["surfaced", "withheld_logged", "sampled_quiet_interval"] | ||
|
|
||
|
|
||
| def write_insight_record(record: dict[str, Any], state_dir: Path) -> Path: | ||
| _validate_with_agent_core(record) | ||
| root = state_dir.expanduser().resolve() / "insights" | ||
| root.mkdir(parents=True, exist_ok=True) | ||
| day = datetime.now(UTC).strftime("%Y-%m-%d") | ||
| path = root / f"{day}.jsonl" | ||
| path.open("a", encoding="utf-8").write(json.dumps(record, sort_keys=True, separators=(",", ":")) + "\n") | ||
| return path | ||
|
|
||
|
|
||
| def _validate_with_agent_core(record: dict[str, Any]) -> None: | ||
| """Validate when the released insight contract is installed. | ||
|
|
||
| Older deployments may still carry an agent-core version that predates the | ||
| insight contract; those keep emitting the already-compatible JSON until the | ||
| repo dependency is bumped. | ||
| """ | ||
|
|
||
| try: | ||
| contracts = importlib.import_module("agent_core.contracts") | ||
| model = getattr(contracts, "InsightDecisionRecord") | ||
| except (ImportError, AttributeError): | ||
| return | ||
| model.model_validate(record) | ||
|
|
||
|
|
||
| def signal_insight_record( | ||
| *, | ||
| repo: str, | ||
| source: str, | ||
| identifier: str, | ||
| title: str, | ||
| context: str, | ||
| action_items: tuple[str, ...] | list[str], | ||
| related: tuple[str, ...] | list[str], | ||
| fingerprint: str, | ||
| action_selected: InsightAction, | ||
| sampling_class: SamplingClass, | ||
| why_now: str, | ||
| issue_ref: str = "", | ||
| policy_version: str = "engineering-intake.v1", | ||
| ) -> dict[str, Any]: | ||
| utility = _utility_for_signal(action_items=action_items, related=related, issue_ref=issue_ref) | ||
| cost = _interruption_cost_for_action(action_selected=action_selected, why_now=why_now) | ||
| return _base_record( | ||
| fingerprint=fingerprint, | ||
| sampling_class=sampling_class, | ||
| candidate_type="signal", | ||
| candidate_source=f"engineering_intake:{source}", | ||
| action_selected=action_selected, | ||
| why_now=why_now, | ||
| support_facts=[title, context, *list(action_items)[:4]], | ||
| evidence_refs=[{"kind": "repo", "ref": repo}, *[{"kind": "related", "ref": item} for item in list(related)[:6]]], | ||
| expected_utility=utility, | ||
| interruption_cost=cost, | ||
| confidence=0.75 if action_selected != "stay_silent" else 0.65, | ||
| policy_version=policy_version, | ||
| budget_context={"repo": repo, "issue_ref": issue_ref}, | ||
| ) | ||
|
|
||
|
|
||
| def governor_insight_record( | ||
| *, | ||
| issue_id: str, | ||
| title: str, | ||
| routing_decision: str, | ||
| reasons: list[str], | ||
| labels: list[str], | ||
| policy_version: str, | ||
| action_selected: InsightAction | None = None, | ||
| sampling_class: SamplingClass = "surfaced", | ||
| ) -> dict[str, Any]: | ||
| action = action_selected or _action_for_routing_decision(routing_decision) | ||
| why_now = "; ".join(reasons[:3]) or f"Governor routed issue as {routing_decision}." | ||
| return _base_record( | ||
| fingerprint=_stable_hash(f"governor:{issue_id}:{routing_decision}"), | ||
| sampling_class=sampling_class, | ||
| candidate_type="github_issue", | ||
| candidate_source="reliability_governor", | ||
| action_selected=action, | ||
| why_now=why_now, | ||
| support_facts=[title, routing_decision, *reasons[:6]], | ||
| evidence_refs=[{"kind": "github_issue", "ref": issue_id}, *[{"kind": "label", "ref": label} for label in labels]], | ||
| expected_utility={ | ||
| "total": 0.7 if action != "stay_silent" else 0.15, | ||
| "components": {"routing_decision": 0.7 if action != "stay_silent" else 0.15}, | ||
| "rationale": [routing_decision], | ||
| }, | ||
| interruption_cost=_interruption_cost_for_action(action_selected=action, why_now=why_now), | ||
| confidence=0.8, | ||
| policy_version=policy_version, | ||
| budget_context={"issue_id": issue_id}, | ||
| ) | ||
|
|
||
|
|
||
| def daemon_insight_record( | ||
| *, | ||
| action_selected: InsightAction, | ||
| sampling_class: SamplingClass, | ||
| why_now: str, | ||
| issue_ref: str = "", | ||
| policy_version: str = "engineering-daemon.v1", | ||
| budget_context: dict[str, Any] | None = None, | ||
| ) -> dict[str, Any]: | ||
| return _base_record( | ||
| fingerprint=_stable_hash(f"daemon:{issue_ref}:{why_now}"), | ||
| sampling_class=sampling_class, | ||
| candidate_type="approved_queue", | ||
| candidate_source="engineering_daemon", | ||
| action_selected=action_selected, | ||
| why_now=why_now, | ||
| support_facts=[why_now, issue_ref] if issue_ref else [why_now], | ||
| evidence_refs=[{"kind": "github_issue", "ref": issue_ref}] if issue_ref else [], | ||
| expected_utility={ | ||
| "total": 0.75 if action_selected == "draft" else 0.05, | ||
| "components": {"approved_queue": 0.75 if action_selected == "draft" else 0.05}, | ||
| "rationale": [why_now], | ||
| }, | ||
| interruption_cost=_interruption_cost_for_action(action_selected=action_selected, why_now=why_now), | ||
| confidence=0.8, | ||
| policy_version=policy_version, | ||
| budget_context=budget_context or {}, | ||
| ) | ||
|
|
||
|
|
||
| def _base_record( | ||
| *, | ||
| fingerprint: str, | ||
| sampling_class: SamplingClass, | ||
| candidate_type: str, | ||
| candidate_source: str, | ||
| action_selected: InsightAction, | ||
| why_now: str, | ||
| support_facts: list[str], | ||
| evidence_refs: list[dict[str, str]], | ||
| expected_utility: dict[str, Any], | ||
| interruption_cost: dict[str, Any], | ||
| confidence: float, | ||
| policy_version: str, | ||
| budget_context: dict[str, Any], | ||
| ) -> dict[str, Any]: | ||
| now = datetime.now(UTC).isoformat() | ||
| return { | ||
| "schema_version": "0.1.0", | ||
| "insight_id": f"ins_eng_{_stable_hash(f'{fingerprint}:{now}')}", | ||
| "loop": "engineering", | ||
| "created_at": now, | ||
| "fingerprint": fingerprint, | ||
| "sampling_class": sampling_class, | ||
| "candidate_type": candidate_type, | ||
| "candidate_source": candidate_source, | ||
| "state_snapshot_refs": [], | ||
| "support_facts": [str(item) for item in support_facts if str(item).strip()][:10], | ||
| "evidence_refs": evidence_refs, | ||
| "action_space": ["notify", "question", "draft", "stay_silent"], | ||
| "action_selected": action_selected, | ||
| "why_now": why_now, | ||
| "why_not_other_actions": _why_not_other_actions(action_selected), | ||
| "expected_utility": expected_utility, | ||
| "interruption_cost": interruption_cost, | ||
| "confidence": confidence, | ||
| "risk_class": "medium", | ||
| "policy_version": policy_version, | ||
| "tool_versions": {}, | ||
| "budget_context": budget_context, | ||
| } | ||
|
|
||
|
|
||
| def _utility_for_signal( | ||
| *, action_items: tuple[str, ...] | list[str], related: tuple[str, ...] | list[str], issue_ref: str | ||
| ) -> dict[str, Any]: | ||
| action_component = min(0.4, len(action_items) * 0.12) | ||
| grounding_component = min(0.35, len(related) * 0.08) | ||
| dedupe_component = 0.0 if issue_ref else 0.2 | ||
| total = round(min(1.0, action_component + grounding_component + dedupe_component), 3) | ||
| return { | ||
| "total": total, | ||
| "components": { | ||
| "actionability": action_component, | ||
| "grounding": grounding_component, | ||
| "novelty": dedupe_component, | ||
| }, | ||
| "rationale": ["read-only mined signal", "candidate issue protocol"], | ||
| } | ||
|
|
||
|
|
||
| def _interruption_cost_for_action(*, action_selected: InsightAction, why_now: str) -> dict[str, Any]: | ||
| cost = 0.15 if action_selected in {"draft", "stay_silent"} else 0.35 | ||
| if "dedupe" in why_now.lower() or "unchanged" in why_now.lower(): | ||
| cost += 0.25 | ||
| return { | ||
| "total": round(min(1.0, cost), 3), | ||
| "components": {"operator_attention": cost}, | ||
| "rationale": [why_now], | ||
| } | ||
|
|
||
|
|
||
| def _action_for_routing_decision(routing_decision: str) -> InsightAction: | ||
| if routing_decision in {"needs_context", "knowledge_gap", "needs_human"}: | ||
| return "question" | ||
| if routing_decision in {"allow_candidate", "allow_approved"}: | ||
| return "draft" | ||
| return "stay_silent" | ||
|
|
||
|
|
||
| def _why_not_other_actions(action_selected: InsightAction) -> dict[str, str]: | ||
| reasons: dict[str, str] = {} | ||
| if action_selected != "notify": | ||
| reasons["notify"] = "A durable issue/decision artifact is more appropriate than a transient notification." | ||
| if action_selected != "question": | ||
| reasons["question"] = "The available evidence is sufficient for the selected route." | ||
| if action_selected != "draft": | ||
| reasons["draft"] = "The candidate is not ready for draft work under current policy." | ||
| if action_selected != "stay_silent": | ||
| reasons["stay_silent"] = "The candidate is relevant now and should remain visible." | ||
| return reasons | ||
|
|
||
|
|
||
| def _stable_hash(value: str) -> str: | ||
| return hashlib.sha256(value.encode("utf-8")).hexdigest()[:16] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| { | ||
| "insights": [ | ||
| { | ||
| "schema_version": "0.1.0", | ||
| "insight_id": "ins_eng_question", | ||
| "loop": "engineering", | ||
| "fingerprint": "eng-question", | ||
| "sampling_class": "surfaced", | ||
| "candidate_type": "github_issue", | ||
| "candidate_source": "reliability_governor", | ||
| "support_facts": ["Issue needs context", "needs_context"], | ||
| "evidence_refs": [{"kind": "github_issue", "ref": "AS215932/engineering-loop#32"}], | ||
| "action_selected": "question", | ||
| "why_now": "Governor needs missing context before routing.", | ||
| "policy_version": "engineering-insight.fixture" | ||
| }, | ||
| { | ||
| "schema_version": "0.1.0", | ||
| "insight_id": "ins_eng_draft", | ||
| "loop": "engineering", | ||
| "fingerprint": "eng-draft", | ||
| "sampling_class": "surfaced", | ||
| "candidate_type": "approved_queue", | ||
| "candidate_source": "engineering_daemon", | ||
| "support_facts": ["Approved work is ready"], | ||
| "evidence_refs": [{"kind": "github_issue", "ref": "AS215932/engineering-loop#33"}], | ||
| "action_selected": "draft", | ||
| "why_now": "Approved queue item can be drafted.", | ||
| "policy_version": "engineering-insight.fixture" | ||
| }, | ||
| { | ||
| "schema_version": "0.1.0", | ||
| "insight_id": "ins_eng_silent", | ||
| "loop": "engineering", | ||
| "fingerprint": "eng-silent", | ||
| "sampling_class": "withheld_logged", | ||
| "candidate_type": "signal", | ||
| "candidate_source": "engineering_intake:fixture", | ||
| "support_facts": ["Duplicate signal already has an open issue"], | ||
| "evidence_refs": [{"kind": "related", "ref": "AS215932/engineering-loop#32"}], | ||
| "action_selected": "stay_silent", | ||
| "why_now": "Existing issue already covers the signal.", | ||
| "policy_version": "engineering-insight.fixture" | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| from pathlib import Path | ||
|
|
||
| from agent_core.contracts import InsightDecisionRecord | ||
|
|
||
| from hyrule_engineering_loop.insights import ( | ||
| daemon_insight_record, | ||
| governor_insight_record, | ||
| signal_insight_record, | ||
| ) | ||
|
|
||
|
|
||
| FIXTURE = Path(__file__).resolve().parent / "fixtures" / "insights" / "decisions.json" | ||
|
|
||
|
|
||
| def test_engineering_insight_fixtures_validate_against_agent_core_contract() -> None: | ||
| payload = json.loads(FIXTURE.read_text(encoding="utf-8")) | ||
|
|
||
| actions: set[str] = set() | ||
| for row in payload["insights"]: | ||
| actions.add(str(row["action_selected"])) | ||
| InsightDecisionRecord.model_validate(row) | ||
|
|
||
| assert actions == {"question", "draft", "stay_silent"} | ||
|
|
||
|
|
||
| def test_engineering_insight_builders_emit_agent_core_records() -> None: | ||
| rows = [ | ||
| signal_insight_record( | ||
| repo="AS215932/engineering-loop", | ||
| source="fixture", | ||
| identifier="sig-1", | ||
| title="Fixture signal", | ||
| context="A read-only mined signal exists.", | ||
| action_items=["review"], | ||
| related=["AS215932/engineering-loop#32"], | ||
| fingerprint="sig-fp", | ||
| action_selected="stay_silent", | ||
| sampling_class="withheld_logged", | ||
| why_now="Existing issue already covers the signal.", | ||
| ), | ||
| governor_insight_record( | ||
| issue_id="AS215932/engineering-loop#32", | ||
| title="Needs context", | ||
| routing_decision="needs_context", | ||
| reasons=["Knowledge context is missing."], | ||
| labels=["loop:needs-context"], | ||
| policy_version="fixture-governor.v1", | ||
| ), | ||
| daemon_insight_record( | ||
| action_selected="draft", | ||
| sampling_class="surfaced", | ||
| why_now="Approved queue item can be drafted.", | ||
| issue_ref="AS215932/engineering-loop#33", | ||
| policy_version="fixture-daemon.v1", | ||
| ), | ||
| ] | ||
|
|
||
| for row in rows: | ||
| InsightDecisionRecord.model_validate(row) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the Governor returns
allow_candidate, it intentionally leaves the issue atloop:candidatefor human review, while onlyloop:approvedis consumed by the daemon for draft PR work. This branch records bothallow_candidateandallow_approvedasaction_selected="draft", so insight logs or arbitration built on these records will report/learn a draft action for issues that were not approved to draft. Reservedraftforallow_approvedand use a human-facing action forallow_candidate.Useful? React with 👍 / 👎.