Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions .github/workflows/tunnel-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ jobs:
if [ -n "$err" ]; then
echo "curl_stderr=$err"
fi
# Ephemeral tunnels (pinggy-free, etc.) expire; DNS/connect 000 is inventory, not dual-gate.
if [ "${code:-000}" = "000" ] || [ "$curl_rc" -ne 0 ]; then
echo "::notice::tunnel unreachable (code=${code:-000} rc=$curl_rc); skip canary (not dual-gate)"
# Ephemeral tunnels expire. DNS/connect 000 is inventory, not dual-gate.
# A live host that does not advertise /health (404) is the same class.
if [ "${code:-000}" = "000" ] || [ "$curl_rc" -ne 0 ] || [ "$code" = "404" ]; then
echo "::notice::tunnel canary skipped (code=${code:-000} rc=$curl_rc); not dual-gate"
exit 0
fi
[ "$code" = "200" ] || { echo "::error::tunnel health $code"; exit 1; }
49 changes: 36 additions & 13 deletions archwiz/context_relationships/seed_merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,28 @@
except ImportError: # Supports direct script use.
from compiler import CompilationError, load_json

MERGER_ID = "archwiz.context_relationships.seed_merger@1.2"
MERGER_ID = "archwiz.context_relationships.seed_merger@1.3"

# Line spans are observations of the same symbol, not identity. Collectors
# disagree when one parse includes a decorator or trailing blank and another
# does not. Missing observed_at must not fail the historical backfill.
# does not. Historical seeds may store the same span as int or numeric string.
# Missing observed_at must not fail the historical backfill.
SPAN_ATTRIBUTE_KEYS = {"line", "start_line", "end_line"}


def node_reference(record: Mapping[str, Any]) -> str:
kind = record.get("kind")
external_id = record.get("external_id")
if not isinstance(kind, str) or not isinstance(external_id, str):
raise CompilationError("seed node must contain string kind and external_id")
return f"{kind}:{external_id}"
def span_line(value: Any) -> int | None:
"""Coerce a span observation to int. Bool is not a line number."""
if isinstance(value, bool) or value is None:
return None
if isinstance(value, int):
return value
if isinstance(value, float) and value.is_integer():
return int(value)
if isinstance(value, str):
text = value.strip()
if text.isdigit() or (text.startswith("-") and text[1:].isdigit()):
return int(text)
return None


def merge_attributes(
Expand All @@ -44,11 +52,18 @@ def merge_attributes(
timestamps_available = isinstance(existing_observed_at, str) and isinstance(incoming_observed_at, str)
for key, value in right.items():
if key in merged and merged[key] != value:
if key in SPAN_ATTRIBUTE_KEYS and isinstance(merged[key], int) and isinstance(value, int):
if key == "end_line":
merged[key] = max(merged[key], value)
else:
merged[key] = min(merged[key], value)
if key in SPAN_ATTRIBUTE_KEYS:
left_line = span_line(merged[key])
right_line = span_line(value)
if left_line is None and right_line is None:
continue
if left_line is None:
merged[key] = right_line
continue
if right_line is None:
merged[key] = left_line
continue
merged[key] = max(left_line, right_line) if key == "end_line" else min(left_line, right_line)
continue
if not timestamps_available:
raise CompilationError(f"node {node_ref} has contradictory attribute {key!r}")
Expand All @@ -59,6 +74,14 @@ def merge_attributes(
return merged


def node_reference(record: Mapping[str, Any]) -> str:
kind = record.get("kind")
external_id = record.get("external_id")
if not isinstance(kind, str) or not isinstance(external_id, str):
raise CompilationError("seed node must contain string kind and external_id")
return f"{kind}:{external_id}"


def canonical_github_blob_url(url: str, owner: str, name: str, ref: str) -> str:
"""Rewrite blob locators onto the seed ref. File identity is the path, not the blob SHA."""
marker = f"https://github.com/{owner}/{name}/blob/"
Expand Down
30 changes: 30 additions & 0 deletions tests/test_context_relationship_seed_merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,33 @@ def test_merge_seeds_unions_symbol_spans_without_observed_at():

assert merged["nodes"][0]["attributes"]["end_line"] == 120
assert merged["nodes"][0]["attributes"]["line"] == 36


def test_merge_seeds_coerces_string_end_line_without_observed_at():
first = {
**BASE,
"nodes": [
{
"kind": "symbol",
"external_id": "multi-ai-cli/backends/deepseek.py:DeepSeekBackend:37",
"attributes": {"name": "DeepSeekBackend", "line": 37, "end_line": 80},
}
],
"edges": [],
}
second = {
**BASE,
"nodes": [
{
"kind": "symbol",
"external_id": "multi-ai-cli/backends/deepseek.py:DeepSeekBackend:37",
"attributes": {"name": "DeepSeekBackend", "line": "37", "end_line": "140"},
}
],
"edges": [],
}

merged, _ = merge_seeds(first, second)

assert merged["nodes"][0]["attributes"]["end_line"] == 140
assert merged["nodes"][0]["attributes"]["line"] == 37
Loading