omnilane 0.42.9 → 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.
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://antigravity.google/schemas/v1/plugin.json",
3
3
  "name": "omnilane",
4
- "version": "0.42.9",
4
+ "version": "0.44.0",
5
5
  "description": "One routing table, every harness: classify subtasks into lanes and delegate through compatible caller-owned native agents or vendor CLIs with exact-AA downward policy and supervised jobs."
6
6
  }
@@ -35,7 +35,7 @@ MODE="advise"; WORKDIR="$PWD"; BACKGROUND=0; DRY_RUN=0
35
35
  OVERRIDE_VENDOR=""; OVERRIDE_MODEL=""; OVERRIDE_EFFORT=""; OVERRIDE_TIMEOUT=""
36
36
  OVERRIDE_JOB_TIMEOUT=""; OVERRIDE_IDLE_TIMEOUT=""; SESSION_REQUEST="auto"
37
37
  THREAD_NAME=""; THREAD_MODE=""; THREAD_ID=""; THREAD_TURN=""; THREAD_CREATED=""
38
- EXECUTOR="auto"; NATIVE_CONTEXT=""; RESOLVE_WITH_CONTEXT=0
38
+ EXECUTOR="auto"; NATIVE_CONTEXT=""; RESOLVE_WITH_CONTEXT=0; INHERIT=0
39
39
  SELECTED_EXECUTOR="cli"; EXECUTOR_REASON="no-native-context"
40
40
  AA_POLICY_FILE="${OMNILANE_AA_POLICY_FILE:-$OMNILANE_REPO/config/aa-model-policy.json}"
41
41
  AA_CALLER_CONTEXT="${OMNILANE_AA_CALLER_CONTEXT:-}"
@@ -67,6 +67,8 @@ flags:
67
67
  before any provider call or job state
68
68
  --executor auto|native|cli caller-owned native handoff or legacy CLI
69
69
  --native-context FILE explicit JSON capabilities; native is not a binary
70
+ --inherit native worker on the caller's own model and effort; resolves no
71
+ lane target, needs --native-context with inherits_caller_runtime
70
72
  --caller-context FILE exact model caller identity plus inherited ceiling
71
73
  --operator-asserted-human explicit AA model-ceiling exemption; assertion only
72
74
  --aa-policy FILE frozen AA policy registry (default: repo config)
@@ -368,6 +370,18 @@ routing_candidate_available() {
368
370
  fi
369
371
  }
370
372
 
373
+ aa_print_refusal() {
374
+ # Refusal JSON on stderr, with the lanes this caller can still reach appended.
375
+ local helper="$OMNILANE_REPO/scripts/lib/aa_lanes.py"
376
+ if [[ -n "$AA_LAST_DECISION" && -r "$helper" ]] && command -v python3 >/dev/null 2>&1; then
377
+ printf '%s\n' "$AA_LAST_DECISION" | python3 "$helper" --registry "$AA_POLICY_FILE" --lane "${LANE:-}" \
378
+ --routing "$OMNILANE_HOME/routing.local.yaml" --routing "$OMNILANE_REPO/routing.yaml" >&2 \
379
+ || printf '%s\n' "$AA_LAST_DECISION" >&2
380
+ else
381
+ printf '%s\n' "$AA_LAST_DECISION" >&2
382
+ fi
383
+ }
384
+
371
385
  aa_policy_decide() {
372
386
  # vendor model effort [target-config] -> structured JSON in AA_LAST_DECISION
373
387
  local vendor="$1" model="$2" effort="$3" target_config="${4:-}" rc=0
@@ -712,6 +726,7 @@ while [[ $# -gt 0 ]]; do
712
726
  }
713
727
  SESSION_REQUEST="single-shot"; shift ;;
714
728
  --dry-run) DRY_RUN=1; shift ;;
729
+ --inherit) INHERIT=1; shift ;;
715
730
  --operator-asserted-human)
716
731
  AA_OPERATOR_ASSERTED_HUMAN=1; shift ;;
717
732
  --mode|--workdir|--vendor|--model|--effort|--timeout|--job-timeout|--idle-timeout|--thread|--executor|--native-context|--caller-context|--aa-policy|--target-config|--transport-overlay)
@@ -823,6 +838,34 @@ case "$EXECUTOR" in
823
838
  esac
824
839
 
825
840
  CHAIN="$(raw_lane_line "$LANE")" || { echo "omnilane: unknown lane '$LANE' (try --list)" >&2; exit 2; }
841
+ if [[ "$INHERIT" -eq 1 ]]; then
842
+ # A worker that inherits this caller's own model and effort runs inside the
843
+ # harness: no lane target is resolved, no vendor CLI or transport overlay is
844
+ # involved, and the lane is only a label for what the work is.
845
+ [[ -z "$OVERRIDE_VENDOR$OVERRIDE_MODEL$OVERRIDE_EFFORT$AA_TARGET_CONFIG" ]] || {
846
+ echo "omnilane: --inherit takes no --vendor, --model, --effort or --target-config; it overrides nothing" >&2
847
+ exit 2
848
+ }
849
+ [[ "$EXECUTOR" != "cli" ]] || { echo "omnilane: --inherit is native only" >&2; exit 2; }
850
+ command -v python3 >/dev/null 2>&1 || { echo "omnilane: native protocol requires Python 3" >&2; exit 2; }
851
+ INHERIT_TIMEOUT="${OVERRIDE_TIMEOUT:-${OMNILANE_TIMEOUT:-600}}"
852
+ [[ "$INHERIT_TIMEOUT" =~ ^[1-9][0-9]*$ ]] || {
853
+ echo "omnilane: invalid timeout (want a positive integer of seconds)" >&2; exit 2
854
+ }
855
+ INHERIT_ARGS=(route --inherit --home "$OMNILANE_HOME" --executor native --lane "$LANE"
856
+ --workdir "$WORKDIR" --mode "$MODE" --task="$TASK" --session "$SESSION_REQUEST"
857
+ --thread "$THREAD_NAME" --policy "$AA_POLICY_FILE" --timeout "$INHERIT_TIMEOUT"
858
+ --job-timeout "${OVERRIDE_JOB_TIMEOUT:-}" --idle-timeout "${OVERRIDE_IDLE_TIMEOUT:-}")
859
+ [[ -z "$NATIVE_CONTEXT" ]] || INHERIT_ARGS+=(--context "$NATIVE_CONTEXT")
860
+ if [[ "$AA_OPERATOR_ASSERTED_HUMAN" == "1" ]]; then
861
+ INHERIT_ARGS+=(--operator-asserted-human)
862
+ elif [[ -n "$AA_CALLER_CONTEXT" ]]; then
863
+ INHERIT_ARGS+=(--caller-context "$AA_CALLER_CONTEXT")
864
+ fi
865
+ [[ "$BACKGROUND" -eq 0 ]] || INHERIT_ARGS+=(--background)
866
+ [[ "$DRY_RUN" -eq 0 ]] || INHERIT_ARGS+=(--dry-run)
867
+ exec python3 "$OMNILANE_REPO/scripts/lib/native.py" "${INHERIT_ARGS[@]}"
868
+ fi
826
869
  if [[ -n "$OVERRIDE_VENDOR" ]]; then
827
870
  if resolve_chain "$CHAIN" "$OVERRIDE_VENDOR"; then
828
871
  :
@@ -835,7 +878,7 @@ if [[ -n "$OVERRIDE_VENDOR" ]]; then
835
878
  exit 4
836
879
  ;;
837
880
  6)
838
- printf '%s\n' "$AA_LAST_DECISION" >&2
881
+ aa_print_refusal
839
882
  exit 3
840
883
  ;;
841
884
  5)
@@ -852,11 +895,11 @@ else
852
895
  resolve_rc=0
853
896
  resolve_chain "$CHAIN" || resolve_rc=$?
854
897
  if [[ "$resolve_rc" -eq 6 ]]; then
855
- printf '%s\n' "$AA_LAST_DECISION" >&2
898
+ aa_print_refusal
856
899
  exit 3
857
900
  elif [[ "$resolve_rc" -ne 0 ]]; then
858
901
  echo "omnilane: no eligible available target for lane '$LANE' (chain:$CHAIN)." >&2
859
- [[ -z "$AA_LAST_DECISION" ]] || printf '%s\n' "$AA_LAST_DECISION" >&2
902
+ [[ -z "$AA_LAST_DECISION" ]] || aa_print_refusal
860
903
  exit 4
861
904
  fi
862
905
  fi
@@ -1110,6 +1153,13 @@ if [[ "$DRY_RUN" -eq 1 ]]; then
1110
1153
  fi
1111
1154
 
1112
1155
  printf 'omnilane: executor=cli reason=%s\n' "$EXECUTOR_REASON" >&2
1156
+ if [[ "$EXECUTOR_REASON" == "no-native-context" && -n "$AA_CALLER_CONTEXT" && -r "$AA_CALLER_CONTEXT" ]]; then
1157
+ CALLER_VENDOR="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["caller"]["vendor"])' "$AA_CALLER_CONTEXT" 2>/dev/null || true)"
1158
+ if [[ -n "$CALLER_VENDOR" && "$CALLER_VENDOR" == "$VENDOR" ]]; then
1159
+ # Same vendor is not same model, so this is an offer, never a silent switch.
1160
+ echo "omnilane: the target is this harness's own vendor, yet it goes out through an external CLI because no capability file was given. To use your own sub-agent tool: omnilane native-context --workdir \"$WORKDIR\", then pass --native-context FILE (or --inherit for a worker on your own model and effort)" >&2
1161
+ fi
1162
+ fi
1113
1163
  mkdir -p "$OMNILANE_HOME"
1114
1164
  if [[ ! -d "$JOBS_ROOT" ]]; then
1115
1165
  mkdir -m 700 "$JOBS_ROOT"
package/scripts/doctor.sh CHANGED
@@ -419,7 +419,13 @@ overlay_path="$(
419
419
  printf '%s' "${OMNILANE_AA_TRANSPORT_OVERLAY:-}"
420
420
  )"
421
421
  if [[ -z "$overlay_path" ]]; then
422
- report PASS transport-overlay "no overlay configured; every runtime mapping stays unverified"
422
+ # Only a model caller needs the overlay; a host whose operator asserts the human
423
+ # exemption is complete without one, and --strict must not fail it.
424
+ if [[ "${OMNILANE_AA_OPERATOR_ASSERTED_HUMAN:-0}" == "1" ]]; then
425
+ report PASS transport-overlay "no overlay configured; fine for a human operator, but a model caller would be refused on every lane (README, 'Let your AI assistant drive omnilane')"
426
+ else
427
+ report WARN transport-overlay "no overlay configured, so a model caller is refused on every lane (runtime-mapping-unverified). First install: probe_sweep.py --root ROOT, build_overlay.py --root ROOT, then export OMNILANE_AA_TRANSPORT_OVERLAY in local.sh; see the README, 'Let your AI assistant drive omnilane'"
428
+ fi
423
429
  elif ! command -v python3 >/dev/null 2>&1; then
424
430
  report WARN transport-overlay "python3 is absent; cannot load the AA transport overlay"
425
431
  elif [[ ! -r "$OVERLAY_HEALTH" ]]; then
@@ -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,