omnilane 0.42.8 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env python3
2
+ """Add eligible_lanes to a refused AA decision.
3
+
4
+ A refusal that only says no leaves a model caller guessing. This reads the
5
+ decision on stdin and appends the lanes whose chain still holds a target at or
6
+ below the caller's effective ceiling, by registry score alone: no CLI is probed
7
+ and nothing is dispatched. Output is one JSON line, the decision plus the list.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+ import shlex
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
18
+ import aa_policy # noqa: E402
19
+ import caller_identity # noqa: E402
20
+
21
+
22
+ def lanes(paths: list[Path]) -> dict[str, list[list[str]]]:
23
+ """Lane -> chain segments; the first file naming a lane wins, as in dispatch."""
24
+ table: dict[str, list[list[str]]] = {}
25
+ for path in paths:
26
+ try:
27
+ text = path.read_text(encoding="utf-8")
28
+ except OSError:
29
+ continue
30
+ for line in text.splitlines():
31
+ line = line.split("#", 1)[0]
32
+ if not line.strip() or line[0] in " \t" or ":" not in line:
33
+ continue
34
+ name, chain = line.split(":", 1)
35
+ name = name.strip()
36
+ if not name or name in table or not chain.strip():
37
+ continue
38
+ segments = []
39
+ for segment in chain.split("|"):
40
+ try:
41
+ fields = shlex.split(segment)
42
+ except ValueError:
43
+ fields = []
44
+ if fields:
45
+ segments.append(fields)
46
+ table[name] = segments
47
+ return table
48
+
49
+
50
+ def eligible(registry: dict, table: dict[str, list[list[str]]], ceiling: int) -> list[dict]:
51
+ found = []
52
+ for lane, segments in table.items():
53
+ for fields in segments:
54
+ vendor = fields[0]
55
+ if vendor in ("off", "vote") or len(fields) < 2:
56
+ continue
57
+ effort = fields[2] if len(fields) > 2 and fields[2] != "-" else None
58
+ row, _ = caller_identity.resolve(registry, vendor, fields[1], effort)
59
+ if row is None or row["score"] > ceiling:
60
+ continue
61
+ found.append({
62
+ "lane": lane,
63
+ "target": row["id"],
64
+ "score": row["score"],
65
+ "transport_verified": row["transport_mapping"].get("runtime_verified") is True,
66
+ })
67
+ break
68
+ return found
69
+
70
+
71
+ def lane_requirement(registry: dict, segments: list[list[str]], caller: dict | None) -> dict | None:
72
+ """The cheapest target in one lane, and the caller effort that would reach it."""
73
+ rows = []
74
+ for fields in segments:
75
+ if fields[0] in ("off", "vote") or len(fields) < 2:
76
+ continue
77
+ effort = fields[2] if len(fields) > 2 and fields[2] != "-" else None
78
+ row, _ = caller_identity.resolve(registry, fields[0], fields[1], effort)
79
+ if row is not None:
80
+ rows.append(row)
81
+ if not rows:
82
+ return None
83
+ cheapest = min(rows, key=lambda row: (row["score"], row["id"]))
84
+ required = None
85
+ if isinstance(caller, dict):
86
+ reaching = sorted((row for row in registry["scored_configs"]
87
+ if row["vendor"] == caller.get("vendor") and row["model"] == caller.get("model")
88
+ and row["score"] >= cheapest["score"]),
89
+ key=lambda row: (row["score"], row["id"]))
90
+ if reaching:
91
+ required = reaching[0]["effort"] or "none"
92
+ return {"target": cheapest["id"], "score": cheapest["score"], "required_caller_effort": required}
93
+
94
+
95
+ def main(argv: list[str] | None = None) -> int:
96
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
97
+ parser.add_argument("--registry", required=True)
98
+ parser.add_argument("--routing", action="append", default=[], type=Path,
99
+ help="routing file, highest precedence first; may repeat")
100
+ parser.add_argument("--lane", help="the lane that was refused")
101
+ args = parser.parse_args(argv)
102
+ raw = sys.stdin.read()
103
+ try:
104
+ decision = json.loads(raw)
105
+ ceiling = decision.get("effective_ceiling")
106
+ if decision.get("allowed") is False and type(ceiling) is int:
107
+ registry, _ = aa_policy.load_registry(args.registry)
108
+ table = lanes(args.routing)
109
+ decision["eligible_lanes"] = eligible(registry, table, ceiling)
110
+ if args.lane in table and decision.get("code") == "target-above-effective-ceiling":
111
+ # The decision describes the last candidate tried; the lane may hold a cheaper one.
112
+ need = lane_requirement(registry, table[args.lane], decision.get("caller"))
113
+ if need is not None:
114
+ decision["lane_requirement"] = dict(need, lane=args.lane)
115
+ if decision.get("inherited_ceiling") == decision.get("caller_score"):
116
+ decision["required_caller_effort"] = need["required_caller_effort"]
117
+ effort = need["required_caller_effort"]
118
+ held = (" The harness recorded no effort, so this caller is held to its "
119
+ "model's floor." if decision.get("caller_degraded") else "")
120
+ decision["reason"] = (
121
+ f"lane {args.lane}'s cheapest target {need['target']} scores "
122
+ f"{need['score']}, above this caller's ceiling of {ceiling}.{held} "
123
+ + (f"Relaunch the calling session at effort {effort} or higher, or have "
124
+ "a human operator dispatch it." if effort else
125
+ "No effort of the caller's model reaches it; a stronger caller model "
126
+ "or a human operator has to dispatch it."))
127
+ sys.stdout.write(json.dumps(decision, ensure_ascii=False, separators=(",", ":")) + "\n")
128
+ except (ValueError, OSError, aa_policy.PolicyError, KeyError, TypeError):
129
+ # Guidance is best effort; the refusal itself must always get through.
130
+ sys.stdout.write(raw if raw.endswith("\n") else raw + "\n")
131
+ return 0
132
+
133
+
134
+ if __name__ == "__main__":
135
+ raise SystemExit(main())
@@ -239,7 +239,13 @@ def load_caller(path: str | Path, registry: dict[str, Any],
239
239
  _check(value.get("snapshot_id") == registry["snapshot"]["id"],
240
240
  "caller-context snapshot does not match frozen registry")
241
241
  _check(value.get("kind") == "model", "unsupported caller-context kind")
242
+ # effort_unverified marks a caller degraded to its model's lowest-scored row
243
+ # because the launching harness recorded no effort; it may only be true.
244
+ unverified = value.pop("effort_unverified", None)
245
+ _check(unverified is None or unverified is True, "invalid caller-context effort_unverified")
242
246
  _exact_fields(value, {"schema_version", "snapshot_id", "kind", "caller", "inherited_ceiling"})
247
+ if unverified:
248
+ value["effort_unverified"] = True
243
249
  value["caller"] = _identity(value["caller"], "caller")
244
250
  ceiling = value["inherited_ceiling"]
245
251
  _check(type(ceiling) is int and 0 <= ceiling <= 100,
@@ -345,10 +351,15 @@ def decide(registry: dict[str, Any], registry_sha256: str, *,
345
351
  "file it prints as --caller-context, which dispatch does itself when the "
346
352
  "launching CLI names its model and effort; a human operator passes "
347
353
  "--operator-asserted-human"),
354
+ failed_gate="caller-identity",
355
+ reason="no caller identity reached the gate",
356
+ next_command="omnilane whoami",
348
357
  )
349
358
  return base
350
359
  base["caller_context_sha256"] = caller_sha256
351
360
  caller_identity = caller["caller"]
361
+ degraded = caller.get("effort_unverified") is True
362
+ base["caller_degraded"] = degraded
352
363
  caller_rows = _matching_rows(registry, caller_identity)
353
364
  if len(caller_rows) != 1:
354
365
  detail = _unknown_reason(registry, caller_identity)
@@ -356,9 +367,24 @@ def decide(registry: dict[str, Any], registry_sha256: str, *,
356
367
  code="unknown-caller-config" if not caller_rows else "ambiguous-caller-config",
357
368
  message=detail or "caller exact vendor/model/effort/reasoning/fallback is not uniquely scored",
358
369
  caller=caller_identity,
370
+ failed_gate="caller-identity",
371
+ reason="the caller context names a configuration the frozen registry does not score exactly once",
372
+ next_command="omnilane whoami",
359
373
  )
360
374
  return base
361
375
  caller_row = caller_rows[0]
376
+ model_rows = [row for row in registry["scored_configs"]
377
+ if row["vendor"] == caller_row["vendor"] and row["model"] == caller_row["model"]]
378
+ if degraded and caller_row["score"] != min(row["score"] for row in model_rows):
379
+ base.update(
380
+ code="invalid-degraded-caller",
381
+ message="an effort-unverified caller must name its model's lowest-scored row",
382
+ caller=caller_identity,
383
+ failed_gate="caller-identity",
384
+ reason="effort_unverified was set on a row above the model's floor",
385
+ next_command="omnilane whoami",
386
+ )
387
+ return base
362
388
  effective = min(caller_row["score"], caller["inherited_ceiling"])
363
389
  base.update(
364
390
  caller=caller_identity,
@@ -370,7 +396,14 @@ def decide(registry: dict[str, Any], registry_sha256: str, *,
370
396
  registry, vendor, model, effort, target_config
371
397
  )
372
398
  if target_row is None:
373
- base.update(code=mapping_code, message="target runtime cannot be mapped to one verified exact AA configuration", **detail)
399
+ base.update(
400
+ code=mapping_code,
401
+ message="target runtime cannot be mapped to one verified exact AA configuration",
402
+ failed_gate="target-transport",
403
+ reason=("the caller is identified; this host's transport overlay does not currently "
404
+ f"verify {vendor} {model} at effort {effort}"),
405
+ next_command="omnilane resign --check",
406
+ **detail)
374
407
  return base
375
408
  target_score = target_row["score"]
376
409
  target_identity = _row_identity(target_row)
@@ -381,9 +414,30 @@ def decide(registry: dict[str, Any], registry_sha256: str, *,
381
414
  target_estimated=target_row["estimated"],
382
415
  )
383
416
  if target_score > effective:
417
+ reaching = sorted((row for row in model_rows if row["score"] >= target_score),
418
+ key=lambda row: (row["score"], row["id"]))
419
+ required = reaching[0]["effort"] or "none" if reaching else None
420
+ if caller["inherited_ceiling"] < caller_row["score"]:
421
+ reason = ("this worker inherited a ceiling below the target; only the session that "
422
+ "dispatched it, or a human operator, can reach this target")
423
+ required = None
424
+ elif required is None:
425
+ reason = (f"no scored effort of {caller_row['model']} reaches {target_score}; a "
426
+ "stronger caller model or a human operator has to dispatch this target")
427
+ elif degraded:
428
+ reason = (f"the launching harness recorded no effort, so the caller was held to "
429
+ f"{caller_row['model']}'s floor of {effective}; relaunch the session with an "
430
+ f"explicit effort of {required} or higher")
431
+ else:
432
+ reason = (f"relaunch the calling session at effort {required} or higher, or have a "
433
+ "human operator dispatch this target")
384
434
  base.update(
385
435
  code="target-above-effective-ceiling",
386
436
  message=f"target score {target_score} exceeds effective caller ceiling {effective}",
437
+ failed_gate="downward-ceiling",
438
+ reason=reason,
439
+ required_caller_effort=required,
440
+ next_command="omnilane list",
387
441
  )
388
442
  return base
389
443
  child_context = {
@@ -402,6 +456,105 @@ def decide(registry: dict[str, Any], registry_sha256: str, *,
402
456
  return base
403
457
 
404
458
 
459
+ def decide_inherited(registry: dict[str, Any], registry_sha256: str, *,
460
+ caller: dict[str, Any] | None, caller_sha256: str | None,
461
+ operator_asserted_human: bool = False,
462
+ host_asserted: dict[str, str] | None = None) -> dict[str, Any]:
463
+ """A native worker that inherits the caller's own model and effort unchanged.
464
+
465
+ It runs what the caller runs, so it scores what the caller scores and cannot
466
+ be an upward dispatch, whether or not the caller's effort is known. There is
467
+ no external CLI, so no transport mapping is involved. It is nobody's lane
468
+ target: the decision names no target configuration and must not be reported
469
+ as satisfying one.
470
+
471
+ None of that rests on knowing who the caller is, so a caller whose identity
472
+ cannot be read still gets an inherited worker, on the host's word for its
473
+ vendor and model. That decision is marked unverified, carries no ceiling and
474
+ publishes no child context: a worker that tried to dispatch would have no
475
+ identity to dispatch with.
476
+ """
477
+ base: dict[str, Any] = {
478
+ "schema_version": 1,
479
+ "snapshot_id": registry["snapshot"]["id"],
480
+ "registry_sha256": registry_sha256,
481
+ "allowed": False,
482
+ "code": "deny",
483
+ "message": "AA policy denied dispatch",
484
+ "caller_kind": "operator-asserted-human" if operator_asserted_human else "model",
485
+ "target_request": {"vendor": None, "model": None, "effort": None, "inherit": True},
486
+ "target_config_id": None,
487
+ "caller_score": None,
488
+ "inherited_ceiling": None,
489
+ "effective_ceiling": None,
490
+ "target_score": None,
491
+ "target_estimated": None,
492
+ "child_context": None,
493
+ "evidence_limit": ("the host asserts that a sub-agent spawned without a model override "
494
+ "inherits the caller's runtime; omnilane cannot observe it"),
495
+ }
496
+ if operator_asserted_human:
497
+ base.update(code="inherit-requires-model-caller",
498
+ message="only a model running inside a harness has a runtime to inherit",
499
+ failed_gate="caller-identity",
500
+ reason="a human operator has no sub-agent tool; dispatch a lane instead",
501
+ next_command="omnilane list")
502
+ return base
503
+ if caller is None and host_asserted is not None:
504
+ base.update(allowed=True, code="native-inherited-unverified-caller",
505
+ message="the worker inherits the caller's own runtime, so it cannot score above it; "
506
+ "the caller's vendor and model are the host's statement, not a verified identity",
507
+ caller_kind="model-unverified", caller_identity_verified=False,
508
+ caller_identity_source="host-asserted",
509
+ target={"inherit": True, "vendor": host_asserted["vendor"],
510
+ "model": host_asserted["model"]})
511
+ return base
512
+ if caller is None:
513
+ base.update(code="missing-caller-context",
514
+ message="no caller identity: an inherited worker still has to know whose runtime it inherits",
515
+ failed_gate="caller-identity",
516
+ reason="no caller identity reached the gate and the capability file names no current_model",
517
+ next_command="omnilane whoami # if that cannot read this harness: omnilane "
518
+ "native-context --vendor VENDOR --model MODEL --inherits-caller-runtime")
519
+ return base
520
+ base["caller_context_sha256"] = caller_sha256
521
+ base["caller_identity_verified"] = True
522
+ base["caller_identity_source"] = "caller-context"
523
+ identity = caller["caller"]
524
+ degraded = caller.get("effort_unverified") is True
525
+ rows = _matching_rows(registry, identity)
526
+ if len(rows) != 1:
527
+ base.update(code="unknown-caller-config" if not rows else "ambiguous-caller-config",
528
+ message=_unknown_reason(registry, identity)
529
+ or "caller exact vendor/model/effort/reasoning/fallback is not uniquely scored",
530
+ caller=identity, failed_gate="caller-identity",
531
+ reason="the caller context names a configuration the frozen registry does not score exactly once",
532
+ next_command="omnilane whoami")
533
+ return base
534
+ row = rows[0]
535
+ floor = min(r["score"] for r in registry["scored_configs"]
536
+ if r["vendor"] == row["vendor"] and r["model"] == row["model"])
537
+ if degraded and row["score"] != floor:
538
+ base.update(code="invalid-degraded-caller",
539
+ message="an effort-unverified caller must name its model's lowest-scored row",
540
+ caller=identity, failed_gate="caller-identity",
541
+ reason="effort_unverified was set on a row above the model's floor",
542
+ next_command="omnilane whoami")
543
+ return base
544
+ effective = min(row["score"], caller["inherited_ceiling"])
545
+ child = {"schema_version": 1, "snapshot_id": registry["snapshot"]["id"], "kind": "model",
546
+ "caller": identity, "inherited_ceiling": effective}
547
+ if degraded:
548
+ child["effort_unverified"] = True
549
+ base.update(allowed=True, code="native-inherited-allowed",
550
+ message="the worker inherits the caller's own runtime, so it cannot score above it",
551
+ caller=identity, caller_degraded=degraded, caller_score=row["score"],
552
+ inherited_ceiling=caller["inherited_ceiling"], effective_ceiling=effective,
553
+ target={"inherit": True, "vendor": row["vendor"], "model": row["model"]},
554
+ child_context=child)
555
+ return base
556
+
557
+
405
558
  def _normalized_effort(value: str) -> str | None:
406
559
  return None if value in ("", "-") else value
407
560
 
@@ -6,6 +6,8 @@ corresponds to a probe run under the selected evidence root whose raw
6
6
  stdout/stderr is hashed into the manifest, so the overlay's evidence[] anchors
7
7
  the whole set.
8
8
  """
9
+ from __future__ import annotations
10
+
9
11
  import argparse
10
12
  import hashlib
11
13
  import json
@@ -14,9 +16,13 @@ import shutil
14
16
  import socket
15
17
  from collections import Counter
16
18
  from datetime import datetime, timezone
19
+ import sys
17
20
  from pathlib import Path
18
21
 
19
- REPO = Path(os.environ.get("OMNILANE_REPO", "/Users/vincentw/dev/omnilane"))
22
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
23
+ import cli_provenance # noqa: E402
24
+
25
+ REPO = Path(os.environ.get("OMNILANE_REPO") or Path(__file__).resolve().parents[2])
20
26
  HOME = Path.home()
21
27
  SWEEP_ID = os.environ.get("OMNILANE_TRANSPORT_SWEEP_ID", "overlay-reprobe-20260909")
22
28
  DEFAULT_ROOT = HOME / ".omnilane" / "transport-evidence" / SWEEP_ID
@@ -98,19 +104,21 @@ def cli_path(name: str) -> Path:
98
104
  return Path(found).resolve()
99
105
 
100
106
 
107
+ RUNNERS = {"grok": "run-grok.sh", "codex": "run-codex.sh", "claude": "run-claude.sh",
108
+ "gemini": "run-gemini.sh"}
109
+ CLI_NAMES = {"grok": "grok", "codex": "codex", "claude": "claude", "gemini": "agy"}
110
+
111
+
101
112
  def core_evidence() -> list[tuple[Path, str]]:
102
113
  """Resolved when a build runs, not at import: a host missing one CLI can
103
114
  still load this module to read PROVEN."""
104
- return [
105
- (cli_path("grok"), "grok"),
106
- (REPO / "scripts/runners/run-grok.sh", "grok"),
107
- (cli_path("codex"), "codex"),
108
- (REPO / "scripts/runners/run-codex.sh", "codex"),
109
- (cli_path("claude"), "claude"),
110
- (REPO / "scripts/runners/run-claude.sh", "claude"),
111
- (cli_path("agy"), "gemini"),
112
- (REPO / "scripts/runners/run-gemini.sh", "gemini"),
113
- ]
115
+ anchors = []
116
+ for vendor in ("grok", "codex", "claude", "gemini"):
117
+ if shutil.which(CLI_NAMES[vendor]) is None:
118
+ continue # not installed here: main() signs nothing for it
119
+ anchors += [(cli_path(CLI_NAMES[vendor]), vendor),
120
+ (REPO / "scripts/runners" / RUNNERS[vendor], vendor)]
121
+ return anchors
114
122
 
115
123
 
116
124
  def sha256(path: Path) -> str:
@@ -129,6 +137,12 @@ def main(argv: list[str] | None = None) -> None:
129
137
  default=DEFAULT_ROOT,
130
138
  help=f"probe sweep root (default: {DEFAULT_ROOT})",
131
139
  )
140
+ parser.add_argument(
141
+ "--source",
142
+ default=os.environ.get("OMNILANE_TRANSPORT_SOURCE")
143
+ or f"{socket.gethostname()} / build_overlay {datetime.now(timezone.utc).date().isoformat()}",
144
+ help="free-text provenance recorded in the overlay",
145
+ )
132
146
  args = parser.parse_args(argv)
133
147
  root = args.root.expanduser()
134
148
 
@@ -144,7 +158,11 @@ def main(argv: list[str] | None = None) -> None:
144
158
  if path.exists():
145
159
  entry[suffix] = {"path": str(path), "sha256": sha256(path)}
146
160
  if "json" not in entry:
147
- raise SystemExit(f"missing probe evidence for {cid}: {ev}")
161
+ # A vendor nobody is logged in to was never probed; that is a gap in this
162
+ # host's overlay, not a reason to refuse to sign the vendors that were.
163
+ unproven.append({"config_id": cid, "verdict_reason": "not probed on this host",
164
+ "observed_model": None, "probed_at": None})
165
+ continue
148
166
  descriptor_path = Path(entry["json"]["path"])
149
167
  descriptor = json.loads(descriptor_path.read_text())
150
168
  if not isinstance(descriptor, dict):
@@ -169,11 +187,18 @@ def main(argv: list[str] | None = None) -> None:
169
187
  manifest_path = root / "probe-manifest.json"
170
188
  manifest_path.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n")
171
189
 
190
+ anchors = core_evidence()
191
+ anchored = {vendor for _, vendor in anchors}
172
192
  mappings = []
173
193
  for cid, (selector, runtime_model, _) in sorted(PROVEN.items()):
174
194
  if cid not in manifest["probe_runs"]:
175
195
  continue
176
196
  row = ROWS[cid]
197
+ if row["vendor"] not in anchored:
198
+ # Evidence with no executable to pin it to verifies nothing.
199
+ unproven.append({"config_id": cid, "verdict_reason": "vendor CLI is not installed",
200
+ "observed_model": None, "probed_at": None})
201
+ continue
177
202
  mapping = {
178
203
  "config_id": cid,
179
204
  "identity": {key: row[key] for key in IDENTITY_FIELDS},
@@ -187,18 +212,20 @@ def main(argv: list[str] | None = None) -> None:
187
212
  mapping["cli_flag"] = "--reasoning-effort"
188
213
  mappings.append(mapping)
189
214
 
190
- evidence = [
191
- {"path": str(path), "sha256": sha256(path), "vendor": vendor}
192
- for path, vendor in core_evidence()
193
- ]
215
+ evidence = []
216
+ for path, vendor in anchors:
217
+ anchor = {"path": str(path), "sha256": sha256(path), "vendor": vendor}
218
+ if path.name != RUNNERS[vendor]:
219
+ # The signer an unattended re-sign is later held to; see cli_provenance.
220
+ anchor["codesign"] = cli_provenance.facts(path)
221
+ evidence.append(anchor)
194
222
  evidence.append({"path": str(manifest_path), "sha256": sha256(manifest_path)})
195
223
 
196
224
  overlay = {
197
225
  "schema_version": 1,
198
226
  "snapshot_id": REGISTRY["snapshot"]["id"],
199
227
  "host": socket.gethostname(),
200
- "source": ("claude-code / MacStudio / operator-directed full sweep 2026-09-07; "
201
- "gemini selectors re-probed 2026-09-09 after agy 1.1.27 -> 1.1.28"),
228
+ "source": args.source,
202
229
  "evidence": evidence,
203
230
  "mappings": mappings,
204
231
  "unproven": unproven,
@@ -40,6 +40,8 @@ Selector = tuple[str, Optional[str], Optional[str]]
40
40
  Lookup = Callable[[int], Optional[tuple[int, list[str]]]]
41
41
  EnvironmentLookup = Callable[[int], dict[str, str]]
42
42
  UUID_PATTERN = re.compile(r"[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}\Z")
43
+ CODEX_TOOL_HOSTS = frozenset(("codex-code-mode-host",))
44
+ SHELLS = frozenset(("zsh", "bash", "sh", "dash", "fish"))
43
45
  TURN_END_EVENTS = frozenset(("task_complete", "turn_complete", "turn_aborted"))
44
46
  CODEX_SANDBOX_REFUSAL = (
45
47
  "Codex sandbox (CODEX_SANDBOX=seatbelt) blocked process inspection; omnilane also "
@@ -58,7 +60,9 @@ def _vendor(executable: str) -> str | None:
58
60
  name = path.name
59
61
  if name == "claude" or (path.parent.name == "versions" and path.parent.parent.name == "claude"):
60
62
  return "claude"
61
- if name == "codex":
63
+ # codex-profile-switch launches the desktop app-server as `codex-modified`; its sibling
64
+ # `codex-code-mode-host` is a tool host, not the CLI, so the match stays exact.
65
+ if name in ("codex", "codex-modified"):
62
66
  return "codex"
63
67
  if name == "grok" or (name.startswith("grok-") and path.parent.name == "downloads"):
64
68
  return "grok"
@@ -184,6 +188,17 @@ def resolve(registry: dict, vendor: str, model: str | None,
184
188
  return None, f"no scored configuration for {vendor} {model} at effort {effort}"
185
189
 
186
190
 
191
+ def floor_row(registry: dict, vendor: str, model: str | None) -> dict | None:
192
+ """The lowest-scored row of a known model, for a caller whose effort is unrecorded.
193
+
194
+ Whatever effort actually ran scores at least this much, so the floor can only
195
+ narrow what the caller may dispatch; it never widens it.
196
+ """
197
+ rows = [row for row in registry["scored_configs"]
198
+ if row["vendor"] == vendor and row["model"] == model]
199
+ return min(rows, key=lambda row: (row["score"], row["id"])) if rows else None
200
+
201
+
187
202
  def _thread_environment(entries: list[bytes]) -> dict[str, str]:
188
203
  values = [entry.partition(b"=")[2] for entry in entries
189
204
  if entry.partition(b"=")[0] == b"CODEX_THREAD_ID"]
@@ -279,7 +294,7 @@ def _launcher(pid: int, lookup: Lookup) -> tuple[int, Selector, int | None] | No
279
294
  Nearest wins: a codex worker started by a Claude session is a codex caller.
280
295
  """
281
296
  seen: set[int] = set()
282
- child = None
297
+ below: list[tuple[int, list[str]]] = []
283
298
  for _ in range(MAX_DEPTH):
284
299
  if pid <= 0 or pid in seen:
285
300
  return None
@@ -290,12 +305,25 @@ def _launcher(pid: int, lookup: Lookup) -> tuple[int, Selector, int | None] | No
290
305
  ppid, argv = entry
291
306
  selector = read_selector(argv)
292
307
  if selector is not None:
293
- return pid, selector, child
294
- child = pid
308
+ return pid, selector, _thread_anchor(below)
309
+ below.append((pid, argv))
295
310
  pid = ppid
296
311
  return None
297
312
 
298
313
 
314
+ def _thread_anchor(below: list[tuple[int, list[str]]]) -> int | None:
315
+ """The process whose initial environment codex wrote for this conversation.
316
+
317
+ That is codex's direct child, unless codex runs commands through its own tool
318
+ host: one host serves every conversation of an app-server, so it carries no
319
+ thread id and the process it starts for the command does.
320
+ """
321
+ for pid, argv in reversed(below):
322
+ if not (argv and Path(argv[0]).name in CODEX_TOOL_HOSTS):
323
+ return pid
324
+ return None
325
+
326
+
299
327
  def find_launcher(pid: int, lookup: Lookup = _process) -> tuple[int, Selector] | None:
300
328
  try:
301
329
  found = _launcher(pid, lookup)
@@ -400,11 +428,20 @@ def _rollout_selector(thread: str,
400
428
  event, context_turn, event_turn = ended
401
429
  raise ValueError(f"latest turn_context turn {context_turn} has already ended "
402
430
  f"({event}, turn {event_turn})" + _rollout_age_hint(last_timestamp, now))
403
- for key, value in latest.items():
431
+ for key in ("turn_id", "model"):
432
+ value = latest[key]
404
433
  if not isinstance(value, str) or not value.strip():
405
434
  detail = " (turn unknown)" if key == "turn_id" else ""
406
435
  raise ValueError(f"latest turn_context {key} must be a non-empty string{detail}")
407
- return ("codex", latest["model"], latest["effort"]), f"thread {thread}, turn {latest['turn_id']}"
436
+ effort = latest["effort"]
437
+ source = f"thread {thread}, turn {latest['turn_id']}"
438
+ if effort is None:
439
+ # Heartbeat automations wake a thread without recording an effort. The
440
+ # model is still known, so the caller degrades instead of going blind.
441
+ return ("codex", latest["model"], None), source + ", turn_context records no effort"
442
+ if not isinstance(effort, str) or not effort.strip():
443
+ raise ValueError("latest turn_context effort must be a non-empty string")
444
+ return ("codex", latest["model"], effort), source
408
445
 
409
446
 
410
447
  def read_caller(pid: int, lookup: Lookup = _process,
@@ -442,7 +479,17 @@ def read_caller(pid: int, lookup: Lookup = _process,
442
479
  detail = f"errno {error.errno}" if isinstance(error, OSError) else "invalid environment block"
443
480
  raise ValueError(f"cannot read codex direct child initial environment ({detail})") from None
444
481
  if not inherited or not UUID_PATTERN.fullmatch(inherited):
445
- raise ValueError("codex direct child CODEX_THREAD_ID is missing or not a UUID")
482
+ entry = lookup(child)
483
+ name = Path(entry[1][0]).name if entry and entry[1] else "unknown"
484
+ hint = ""
485
+ if name.lstrip("-") in SHELLS:
486
+ # codex starts `zsh -lc '<command>'`; the shell sets the thread id and, for one
487
+ # simple command, execs into it, so that command is the direct child. With
488
+ # `;`, `&&`, `|` or a subshell the shell stays, and its own start had no id.
489
+ hint = ("; run the omnilane command as the only command of the tool call, "
490
+ "with no `;`, `&&`, `|` or subshell around it")
491
+ raise ValueError(f"codex direct child CODEX_THREAD_ID is missing or not a UUID "
492
+ f"(read from pid {child}, {name}){hint}")
446
493
  if inherited != thread:
447
494
  raise ValueError("CODEX_THREAD_ID mismatch between current process and codex direct child")
448
495
  selector, source = _rollout_selector(thread, current_environment, now)
@@ -461,18 +508,25 @@ def load_registry(path: str | Path) -> tuple[dict, str]:
461
508
  os.environ.update(saved)
462
509
 
463
510
 
464
- def write_context(row: dict, registry: dict, home: Path) -> Path:
511
+ def write_context(row: dict, registry: dict, home: Path, effort_unverified: bool = False) -> Path:
465
512
  """One file per identity: every session launched the same way is the same caller."""
466
513
  directory = Path(home) / "caller-context"
467
514
  directory.mkdir(parents=True, exist_ok=True)
468
- path = directory / (row["id"].replace("/", "--") + ".json")
469
- text = json.dumps({
515
+ stem = row["id"].replace("/", "--")
516
+ if effort_unverified:
517
+ # Not the same caller as one that really launched at the floor effort.
518
+ stem = f"{row['vendor']}--{row['model'].replace('.', '-')}--effort-unverified"
519
+ path = directory / (stem + ".json")
520
+ value = {
470
521
  "schema_version": 1,
471
522
  "snapshot_id": registry["snapshot"]["id"],
472
523
  "kind": "model",
473
524
  "caller": {key: row[key] for key in aa_policy.IDENTITY_FIELDS},
474
525
  "inherited_ceiling": row["score"],
475
- }, indent=2, sort_keys=True) + "\n"
526
+ }
527
+ if effort_unverified:
528
+ value["effort_unverified"] = True
529
+ text = json.dumps(value, indent=2, sort_keys=True) + "\n"
476
530
  try:
477
531
  if path.read_text() == text:
478
532
  return path
@@ -505,14 +559,24 @@ def main(argv: list[str] | None = None, environment: Mapping[str, str] = os.envi
505
559
  print(f"omnilane: cannot read the caller identity: {error}", file=sys.stderr)
506
560
  return 3
507
561
  row, reason = resolve(registry, vendor, model, effort)
562
+ degraded = False
563
+ if row is None and vendor == "codex" and model and effort is None:
564
+ row = floor_row(registry, vendor, model)
565
+ degraded = row is not None
508
566
  if row is None:
509
567
  print(f"omnilane: cannot read the caller identity from pid {pid}: {reason}", file=sys.stderr)
510
568
  return 3
511
569
  home = Path(os.environ.get("OMNILANE_HOME") or Path.home() / ".omnilane")
512
- path = write_context(row, registry, home)
570
+ path = write_context(row, registry, home, effort_unverified=degraded)
513
571
  provenance = f", {source}" if source else ""
514
- print(f"omnilane: caller is {row['id']} (score {row['score']}), read from pid {pid}{provenance}",
515
- file=sys.stderr)
572
+ if degraded:
573
+ print(f"omnilane: caller is {vendor}/{model} at an unrecorded effort; degraded to its "
574
+ f"lowest-scored row {row['id']} (ceiling {row['score']}), read from pid {pid}"
575
+ f"{provenance}. Lanes above that ceiling need a session launched with an "
576
+ f"explicit effort.", file=sys.stderr)
577
+ else:
578
+ print(f"omnilane: caller is {row['id']} (score {row['score']}), read from pid {pid}"
579
+ f"{provenance}", file=sys.stderr)
516
580
  print(path)
517
581
  return 0
518
582