Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d2d82cf
refactor(orchestrator): render full inbox batch, drop compaction bypa…
ZhengGong-amd Jul 29, 2026
6f127ee
refactor(policy): drop the free-form red-line scan
ZhengGong-amd Jul 30, 2026
b641abb
refactor(policy): remove the specialist_done validation path
ZhengGong-amd Jul 30, 2026
140e868
refactor(policy): observe rather than deny soft specialist dispatch s…
ZhengGong-amd Jul 30, 2026
7eeceb1
refactor(state): drop unreachable task-registry surface
ZhengGong-amd Jul 30, 2026
ca46bcd
feat(orchestrator): show specialist health to orchestration
ZhengGong-amd Jul 30, 2026
562b9ac
docs: drop references to the removed dispatch rules
ZhengGong-amd Jul 30, 2026
4de16b0
feat(orchestrator): make specialist failures visible in the inbox line
ZhengGong-amd Jul 30, 2026
ebce3a5
feat(orchestrator): report abandoned retries and expose in-flight tasks
ZhengGong-amd Jul 30, 2026
be9f83c
feat(orchestrator): render the GPU pool and lane capacities
ZhengGong-amd Jul 30, 2026
44b55ee
feat(orchestrator): let orchestration reap work it dispatched
ZhengGong-amd Jul 30, 2026
2893687
feat(orchestrator): add extend_lease so live work can outrun its TTL
ZhengGong-amd Jul 30, 2026
10c6594
feat(specialists): wire the two-way channel to a running specialist
ZhengGong-amd Jul 30, 2026
ac5ad43
fix(orchestrator): repair gaps found reviewing the instrumentation batch
ZhengGong-amd Jul 30, 2026
c9e14e9
refactor(orchestrator): drop defensive scaffolding from the new instr…
ZhengGong-amd Jul 30, 2026
e59e9f6
refactor(specialists): simplify proposal curation target
ZhengGong-amd Jul 30, 2026
f9e453d
fix(specialists): make rebench cleanup-safe
ZhengGong-amd Jul 30, 2026
ccd25f6
docs(specialists): align GPU and source-root guidance
ZhengGong-amd Jul 30, 2026
7ee7fd0
fix(runtime): preserve launch context and model metadata
ZhengGong-amd Jul 30, 2026
8c31e32
Merge remote-tracking branch 'origin/main' into feat/zgong/explore-op…
ZhengGong-amd Jul 30, 2026
be5b81a
chore: exclude temporary review notes
ZhengGong-amd Jul 30, 2026
cd5c8d3
chore: ignore temporary review notes
ZhengGong-amd Jul 30, 2026
0491edb
style: remove trailing blank line
ZhengGong-amd Jul 30, 2026
762a844
fix(orchestrator): drop the specialist block that never saw a specialist
ZhengGong-amd Jul 31, 2026
22c51db
test(orchestrator): cover the reader that now carries specialist visi…
ZhengGong-amd Jul 31, 2026
acad691
fix(orchestrator): make extend_lease actually extend the work it names
ZhengGong-amd Jul 31, 2026
9fc0905
Merge origin/main into feat/zgong/explore-opt-11
ZhengGong-amd Jul 31, 2026
8f1ab9d
test(orchestrator): exercise the extend_lease reaper path, not just i…
ZhengGong-amd Jul 31, 2026
6eb7319
test(orchestrator): lift coverage over the CI gate
ZhengGong-amd Aug 1, 2026
379eb89
fix(orchestrator): preserve late lease extensions
ZhengGong-amd Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ htmlcov/
# Skill run results (large, regenerated by agent)
.cursor/skills/*/results/

# PR description scratchpad. Tools (gh CLI, codex) sometimes drop a
# pr.md at the repo root when drafting a pull request; the
# description belongs in the PR body, not in the tree.
# PR description scratchpads and local review notes.
pr.md
pr_*.md
Specialist.discuss.md
specialist.discuss*.md
specialist.info.md

# F0/F1 integration: keep test baselines, dry-run merge maps, and
# archived regression sweeps tracked.
Expand Down
28 changes: 28 additions & 0 deletions src/hyperloom/agents/kernel/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ TRACELENS_MIRROR_DIR="${TRACELENS_MIRROR_DIR:-${_open_source_root}/TraceLens-int
# missing from env, source $REPO_ROOT/.env but protect already-set values.
REPO_ROOT="${REPO_ROOT:-$(pwd)}"
DOTENV="${REPO_ROOT}/.env"

# Vars whose already-resolved value must survive the `. $REPO_ROOT/.env` below.
# The dotenv is a *credentials* fallback, but it is a full env file: sourcing it
# under `set -a` re-imports every path var it happens to carry. A stale entry —
# or one written by a DIFFERENT checkout / workspace sharing this .env — then
# silently overwrites what the caller resolved, and the run installs against one
# workspace while writing its env file into another (#Hyperloom-2 / atom mixup).
# Only non-empty pre-source values are restored, so the dotenv still fills gaps.
_DOTENV_PROTECTED_VARS='REPO_ROOT KERNEL_AGENT_ROOT HYPERLOOM_KERNEL_AGENT_ROOT
USER_DATA_PATH HYPERLOOM_RUNTIME_DIR KERNEL_AGENT_ENV HYPERLOOM_ROOT
MAGPIE_PATH INFERENCEX_PATH TRACELENS_ROOT TRACELENS_INTERNAL_ROOT
GEAK_ROOT GEAK_E2E_RUNNER PYTHONPATH'

if [ -z "${ANTHROPIC_BASE_URL:-}" ] || [ -z "${ANTHROPIC_API_KEY:-}" ] \
|| [ -z "${ANTHROPIC_AUTH_TOKEN:-}" ] || [ -z "${DEEPSEEK_API_KEY:-}" ] \
|| [ -z "${DEEPSEEK_BASE_URL:-}" ]; then
Expand All @@ -232,6 +245,9 @@ if [ -z "${ANTHROPIC_BASE_URL:-}" ] || [ -z "${ANTHROPIC_API_KEY:-}" ] \
_snap_anthropic_token="${ANTHROPIC_AUTH_TOKEN-}"
_snap_deepseek_key="${DEEPSEEK_API_KEY-}"
_snap_deepseek_url="${DEEPSEEK_BASE_URL-}"
for _v in $_DOTENV_PROTECTED_VARS; do
eval "_snap_prot_${_v}=\"\${${_v}-}\""
done
set -a
# shellcheck disable=SC1091
. "$REPO_ROOT/.env"
Expand All @@ -241,6 +257,18 @@ if [ -z "${ANTHROPIC_BASE_URL:-}" ] || [ -z "${ANTHROPIC_API_KEY:-}" ] \
[ -n "$_snap_anthropic_token" ] && export ANTHROPIC_AUTH_TOKEN="$_snap_anthropic_token"
[ -n "$_snap_deepseek_key" ] && export DEEPSEEK_API_KEY="$_snap_deepseek_key"
[ -n "$_snap_deepseek_url" ] && export DEEPSEEK_BASE_URL="$_snap_deepseek_url"
for _v in $_DOTENV_PROTECTED_VARS; do
eval "_snap_val=\"\${_snap_prot_${_v}-}\""
if [ -n "${_snap_val}" ]; then
eval "_cur_val=\"\${${_v}-}\""
if [ "${_cur_val}" != "${_snap_val}" ]; then
echo "[kernel-agent] .env would have overridden ${_v}=${_snap_val} with ${_cur_val}; keeping the caller's value" >&2
fi
eval "export ${_v}=\"\${_snap_prot_${_v}}\""
fi
unset "_snap_prot_${_v}"
done
unset _v _snap_val _cur_val
unset _snap_anthropic_url _snap_anthropic_key _snap_anthropic_token
unset _snap_deepseek_key _snap_deepseek_url
echo "[kernel-agent] loaded credentials fallback from $REPO_ROOT/.env (env wins)"
Expand Down
9 changes: 6 additions & 3 deletions src/hyperloom/agents/robustness/role/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class IntentType(str, Enum):
RESPONSE = "response"
REVIEW_VERDICT = "review_verdict"
KILL_TASK = "kill_task"
# Robustness never emits this; kept in the mirror for the contract test.
EXTEND_LEASE = "extend_lease"
PRUNE_BRANCH = "prune_branch"
ESCALATE_STRATEGY_CHANGE = "escalate_strategy_change"
# Robustness never emits this; kept in the mirror for the contract test.
Expand Down Expand Up @@ -739,14 +741,15 @@ class IntentSpec:
# Required-field map for intents robustness never emits but the upstream
# contract test still diffs against. No builder/validator: they are here only
# to keep :data:`PAYLOAD_REQUIRED` byte-equal with upstream ``_PAYLOAD_REQUIRED``.
# ``SPECIALIST_DONE`` is the specialist exit envelope (PolicyGate R3 validates
# it); ``REVIEW_VERDICT`` enforces only the structural ``target_proposal_msg_id``
# here (verdict/verdict_map mutual exclusion lives in upstream policy).
# ``SPECIALIST_DONE`` is the specialist exit envelope; ``REVIEW_VERDICT``
# enforces only the structural ``target_proposal_msg_id`` here
# (verdict/verdict_map mutual exclusion lives in upstream policy).
_REQUIRED_ONLY: Mapping[IntentType, tuple[str, ...]] = {
IntentType.PROPOSE_ACTION: ("action_name", "predicted_gain_pct"),
IntentType.REQUEST: ("target_agent", "kind"),
IntentType.RESPONSE: ("in_reply_to", "kind"),
IntentType.REVIEW_VERDICT: ("target_proposal_msg_id",),
IntentType.EXTEND_LEASE: ("task_id", "extra_sec"),
IntentType.SPECIALIST_DONE: (
"gap_canonical_id",
"domain",
Expand Down
19 changes: 12 additions & 7 deletions src/hyperloom/inference_optimizer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,13 +400,18 @@ Steps for the launching agent:
lightweight classify from the model's local `config.json` (decoder
type, attention variant, expert counts, MTP, SWA window) and set
`"source": "config_classify"`.
3. **Write the convention file** — once `session_dir` exists, write the
profile to exactly `<session_dir>/model_arch.json`: the file named
`model_arch.json` in the current session directory. Do not write it to
`$USER_DATA_PATH/model_arch.json`; that path is shared by concurrent
sessions and can be overwritten by another launch. Include `model_name`
(required for the stale-file guard). Set it to the **clean model name**
(e.g. `Qwen2.5-7B-Instruct`);
3. **Write the profile BEFORE launch and point `$HYPERLOOM_MODEL_ARCH_FILE`
at it.** The CLI creates `session_dir` and reads the profile in the *same
process*, so a file written to `<session_dir>/model_arch.json` after the
session dir appears always loses the race — the run seeds
`state.model_arch={}` and the profile never reaches any prompt. Write the
JSON to a launcher-owned path instead and export
`HYPERLOOM_MODEL_ARCH_FILE=<that path>` in the shell that spawns
`optimize`; the CLI copies it into `<session_dir>/model_arch.json` for
provenance. Use a **session-unique** filename — `$USER_DATA_PATH` is shared
by concurrent sessions on WekaFS, so a fixed name races other launches.
Include `model_name` (required for the stale-file guard). Set it to the
**clean model name** (e.g. `Qwen2.5-7B-Instruct`);
the guard normalizes launch forms — flat dirs, HF repo ids, and HF hub
cache `models--org--repo/snapshots/<hash>` paths — so do NOT use the
snapshot commit hash. All other fields are optional; renderers drop
Expand Down
42 changes: 41 additions & 1 deletion src/hyperloom/inference_optimizer/cli/model_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,37 @@ def _resolve_amd_gpu_type(explicit: str | None = None) -> str | None:
_MLX_QUANT_MODES = frozenset({"affine", "mlx"})


def _read_preseeded_model_arch(arch_path: Path) -> str | None:
"""Return the pre-seeded ``$HYPERLOOM_MODEL_ARCH_FILE`` text, or ``None``.

Copies the source into ``arch_path`` so the session keeps its own copy of
the profile the run was seeded with. A failed copy is not fatal — the text
is still returned and the profile still reaches the prompts.

Args:
arch_path (Path): The in-session ``model_arch.json`` destination.

Returns:
str | None: The profile JSON text, or ``None`` when the env is unset or
the file is unreadable.
"""
src = (os.environ.get("HYPERLOOM_MODEL_ARCH_FILE") or "").strip()
if not src:
return None
try:
text = Path(src).expanduser().read_text(encoding="utf-8")
except OSError as exc:
logging.warning("model_arch_preseed_unreadable: %s (%s)", src, exc)
return None
try:
arch_path.parent.mkdir(parents=True, exist_ok=True)
arch_path.write_text(text, encoding="utf-8")
except OSError as exc:
logging.warning("model_arch_preseed_copy_failed: %s -> %s (%s)", src, arch_path, exc)
logging.info("model_arch_preseeded_from: %s", src)
return text


def _load_model_arch(
workspace_root: Path,
model_name: str,
Expand All @@ -324,6 +355,13 @@ def _load_model_arch(
``models--org--repo/snapshots/<hash>`` paths so a declared clean name still
matches a commit-hash launch basename.

The session dir is created and seeded in the same CLI process, so a
launcher following SKILL Step 1.5 ("write it once ``session_dir`` exists")
can never win that race. ``$HYPERLOOM_MODEL_ARCH_FILE`` is the pre-launch
escape hatch: point it at a profile written *before* launch and it is used
when the in-session file is absent, then copied into the session dir so the
run keeps its own provenance copy.

Args:
workspace_root (Path): Directory containing ``model_arch.json``.
model_name (str): The resolved model identity (display name / basename).
Expand All @@ -340,7 +378,9 @@ def _load_model_arch(
try:
raw = arch_path.read_text(encoding="utf-8")
except FileNotFoundError:
return {}
raw = _read_preseeded_model_arch(arch_path)
if raw is None:
return {}
except OSError as exc:
logging.warning("model_arch_unreadable: %s (%s)", arch_path, exc)
return {}
Expand Down
6 changes: 4 additions & 2 deletions src/hyperloom/inference_optimizer/protocol/intent.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class IntentType(str, Enum):
REQUEST = "request"
RESPONSE = "response"
REVIEW_VERDICT = "review_verdict" # Critic-only
KILL_TASK = "kill_task" # Robustness-only; payload.scope must be "task"
KILL_TASK = "kill_task" # payload.scope must be "task"
EXTEND_LEASE = "extend_lease" # refresh a live task's lease TTL
# Robustness-only scheduling police.
PRUNE_BRANCH = "prune_branch"
ESCALATE_STRATEGY_CHANGE = "escalate_strategy_change"
Expand Down Expand Up @@ -61,9 +62,10 @@ class Intent:
# verdict/verdict_map mutual exclusion enforced by _validate_review_verdict_payload.
IntentType.REVIEW_VERDICT: ("target_proposal_msg_id",),
IntentType.KILL_TASK: ("task_id", "reason"),
IntentType.EXTEND_LEASE: ("task_id", "extra_sec"),
IntentType.PRUNE_BRANCH: ("family", "reason"),
IntentType.ESCALATE_STRATEGY_CHANGE: ("reason", "next_action_hint"),
# specialist exit envelope; per-variant schema enforced by PolicyGate R3.
# specialist exit envelope; the runner re-stamps and defaults the payload.
IntentType.SPECIALIST_DONE: ("gap_canonical_id", "domain", "proposal_set", "empty", "summary"),
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def test_orchestration_permissions():
assert IntentType.PRUNE_BRANCH in role.allowed_intents
assert IntentType.ESCALATE_STRATEGY_CHANGE in role.allowed_intents
assert IntentType.REVIEW_VERDICT not in role.allowed_intents
assert IntentType.KILL_TASK not in role.allowed_intents
assert IntentType.KILL_TASK in role.allowed_intents
assert IntentType.RESPONSE not in role.allowed_intents


Expand Down Expand Up @@ -123,7 +123,7 @@ def test_review_verdict_critic_only():


def test_kill_and_robustness_only_renamed():
assert KILL_TASK_SOURCE_ALLOWLIST == frozenset({"robustness"})
assert KILL_TASK_SOURCE_ALLOWLIST == frozenset({"robustness", "orchestration"})
assert ROBUSTNESS_ONLY_SOURCE_ALLOWLIST == frozenset({"robustness"})
assert ROBUSTNESS_ONLY_INTENTS == frozenset(
{
Expand Down Expand Up @@ -527,16 +527,26 @@ def test_gate_robustness_kill_task_ok(gate):
)


def test_gate_orchestration_kill_task_rejected(gate):
def test_gate_orchestration_kill_task_allowed(gate):
gate.validate_intent(
"orchestration",
Intent(
type=IntentType.KILL_TASK,
payload={"task_id": "t1", "reason": "stalled"},
),
)


def test_gate_orchestration_kill_task_process_scope_rejected(gate):
with pytest.raises(PolicyDenied) as exc:
gate.validate_intent(
"orchestration",
Intent(
type=IntentType.KILL_TASK,
payload={"task_id": "t1", "reason": "stalled"},
payload={"task_id": "t1", "reason": "stalled", "scope": "process"},
),
)
assert exc.value.rule == "role"
assert exc.value.rule == "kill_scope"


def test_gate_robustness_kill_task_process_scope_rejected(gate):
Expand Down
20 changes: 0 additions & 20 deletions src/hyperloom/inference_optimizer/tests/test_common_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2429,26 +2429,6 @@ def test_llm_prompt_parse_response_edges() -> None:
assert parse_llm_response("[]") == {"executive_summary": "", "section_narratives": {}}


# ---------------------------------------------------------------------------
# orchestrator.state.orchestration_memory.deterministic_memory_fallback
# ---------------------------------------------------------------------------


def test_deterministic_memory_fallback_bad_gain() -> None:
from hyperloom.orchestrator.state.orchestration_memory import deterministic_memory_fallback

state = SimpleNamespace(
current_best={"tput": 123.0},
optimization_stack=[],
cumulative_gain_validated="not-a-number",
phase="EXPLORE",
macro_cycle=2,
)
record = deterministic_memory_fallback(state)
assert "current_plan" in record
assert "phase=EXPLORE" in record["current_plan"]


# ---------------------------------------------------------------------------
# orchestrator.specialists.profile
# ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -800,9 +800,6 @@ class _Policy:
def should_checkpoint(self, **kw):
return False

def is_hard_compaction(self, n):
return False

coord._checkpoint_policy = _Policy()
assert await coord._maybe_checkpoint_orchestration(tick=5) is False

Expand All @@ -820,9 +817,6 @@ class _Policy:
def should_checkpoint(self, **kw):
return True

def is_hard_compaction(self, n):
return False

coord._checkpoint_policy = _Policy()
took = await coord._maybe_checkpoint_orchestration(tick=12, phase_changed=True)
assert took is True
Expand Down Expand Up @@ -863,7 +857,6 @@ async def _run(**kw):
def test_checkpoint_policy_context_fraction_env(tmp_path, monkeypatch) -> None:
monkeypatch.setenv("USER_DATA_PATH", str(tmp_path))
monkeypatch.setenv("INFERENCE_OPTIMIZER_CTX_SOFT_FRACTION", "0.5")
monkeypatch.setenv("INFERENCE_OPTIMIZER_CTX_HARD_FRACTION", "0.75")
sd = make_session_dir()
from .conftest import seed_target_analysis_marker

Expand All @@ -872,7 +865,6 @@ def test_checkpoint_policy_context_fraction_env(tmp_path, monkeypatch) -> None:
backends["orchestration"].model = "claude-opus-4-8" # type: ignore[attr-defined]
c = Coordinator(sd, backends=backends)
assert c._checkpoint_policy.context_token_soft == 100_000
assert c._checkpoint_policy.context_token_hard == 150_000


def test_orchestration_memory_rollback_env(tmp_path, monkeypatch) -> None:
Expand Down Expand Up @@ -901,9 +893,6 @@ class _Policy:
def should_checkpoint(self, **kw):
return True

def is_hard_compaction(self, n): # default: not hard
return False

coord._checkpoint_policy = _Policy()


Expand Down Expand Up @@ -947,30 +936,6 @@ async def test_checkpoint_degenerate_three_times_emits_medium_observation(coord:
assert any(p.get("severity") == "medium" and p.get("consecutive") == 3 for p in degraded)


@pytest.mark.asyncio
async def test_checkpoint_degenerate_hard_uses_fallback(coord: Coordinator) -> None:
import time

_make_conversational(coord, raw_text="still no JSON")
coord._checkpoint_enabled = True
coord._orchestration_seeded = True
coord._run_started_monotonic = time.monotonic()
coord.shared_state.phase = "EXPLORE"

class _Policy:
def should_checkpoint(self, **kw):
return True

def is_hard_compaction(self, n): # near the window → force
return True

coord._checkpoint_policy = _Policy()
took = await coord._maybe_checkpoint_orchestration(tick=20)
assert took is True
assert coord._orchestration_seeded is False
assert coord.shared_state.orchestration_memory.get("current_plan", "").startswith("[auto]")


@pytest.mark.asyncio
async def test_checkpoint_failure_resets_tracker(coord: Coordinator) -> None:
import time
Expand Down Expand Up @@ -1712,7 +1677,6 @@ async def test_handle_intent_routes_rare_types(coord: Coordinator, monkeypatch)
IntentType.PRUNE_BRANCH: "_handle_prune_branch",
IntentType.ALERT: "_handle_alert",
IntentType.UPDATE_STATE: "_handle_update_state",
IntentType.SPECIALIST_DONE: "_handle_specialist_done",
}
for it, attr in routes.items():

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1367,9 +1367,9 @@ async def runner(ctx):
assert ran["called"] is True
assert res.state == "succeeded"
assert res.result == {"tput": 1.0}
assert any(
"vanished" in rec.message.lower() and "_transition_resilient" in rec.message for rec in caplog.records
), "expected the disappearing-row warning to fire"
assert any("vanished" in rec.message.lower() for rec in caplog.records), (
"expected the disappearing-row warning to fire"
)
db.close()


Expand Down
Loading
Loading