Skip to content

feat(introspection): slim, just-in-time engine introspection worker#114

Draft
rohitg00 wants to merge 7 commits into
mainfrom
feat/introspection-worker
Draft

feat(introspection): slim, just-in-time engine introspection worker#114
rohitg00 wants to merge 7 commits into
mainfrom
feat/introspection-worker

Conversation

@rohitg00
Copy link
Copy Markdown
Contributor

@rohitg00 rohitg00 commented May 11, 2026

Summary

Adds a new introspection worker that wraps the engine's introspection
surfaces (engine::workers::list, engine::functions::list,
skills::list) with progressive disclosure. Default responses are
2-3 KB instead of 50+ KB; full schemas are fetched only on demand.

Today, a discovery-first turn typically calls engine::functions::list
the reply is ~52 KB of JSON (every function's full request/response
schema) and replays in the transcript on every subsequent turn. With
this worker, a typical bootstrap + keyword filter + one describe totals
~5 KB. Net ~25× drop in input tokens by turn 5 on observed sessions.

Function ids registered

Function id Returns Notes
introspection::context::bootstrap session-start frame Connected workers, engine builtins that are NOT enabled (with activation hints — e.g. iii-sandbox config block), root skill index ids only, pinned discovery tips. ~3 KB.
introspection::context::worker_status { status, builtin, activation_hint } Probe one worker. Lets the agent confirm iii-sandbox / iii-http / iii-cron is reachable before calling its functions.
introspection::workers::list { name, status, function_count, description }[] Filters anonymous host:port registrations and dedupes ghost reconnects. include=full returns the raw graph.
introspection::workers::describe one worker + slim functions + skills Embedded function entries trimmed to id + description only (no schemas). ~2 KB vs ~30 KB raw.
introspection::functions::list { id, worker, description }[] Drops 16 noise prefixes by default (skills::resources-*, engine::console::, telemetry::, hook-fanout::, policy-denylist::, auth::, introspection::stream::, etc). include_noise: true opts back in. Returns filtered_out count for transparency.
introspection::functions::describe one function full schema Just-in-time fetch.
introspection::stream::subscribe snapshot today Live deltas land when the engine emits on the introspection.registrations pubsub channel (companion change welcome).
introspection::registry::query filtered results HTTPS GET workers.iii.dev/registry/index.json + substring filter.

Why this exists

Nothing in the tree covers slim, streamable, just-in-time engine
introspection at this shape. sensor is code-quality regression
(unrelated). This worker fills the gap without changing the engine.

introspection::context::bootstrap in particular is the answer to:
"the agent calls engine::functions::list then floods the transcript
with 52 KB of unused schemas."
It returns a 3 KB orientation frame
including a discovery flow and pinned tips telling the agent to use
introspection::functions::list first, then describe one id at a time.

Architecture

Pure proxy. No new state. No new database. No engine changes. Each
function is a thin reshape of an existing engine query, plus a small
static table of known engine builtins so the agent gets activation
hints for the ones that are config-gated.

introspection/
├── Cargo.toml                       # iii-sdk =0.11.6, reqwest, walkdir-free
├── iii.worker.yaml                  # iii: v1, binary deploy, bin: iii-introspection
├── skill.md                         # top-level iii://introspection
├── README.md                        # function table + recommended flow
├── config.yaml                      # registry_url, default_timeout_ms
├── src/
│   ├── main.rs                      # register the 8 functions
│   ├── config.rs                    # yaml load with defaults
│   └── functions/
│       ├── mod.rs                   # DEFAULT_EXCLUDED_NAMESPACES (16),
│       │                            # ENGINE_BUILTINS (10), shared call()
│       ├── context.rs               # bootstrap + worker_status
│       ├── workers.rs               # list + describe
│       ├── functions_mod.rs         # list + describe
│       ├── stream.rs                # snapshot subscribe
│       └── registry.rs              # workers.iii.dev query
└── tests/integration.rs             # slim entry + payload shape tests

SDK pin

Built locally against iii-sdk = "=0.11.6". Happy to bump down to
=0.11.3 to match the rest of the tree if CI prefers — the SDK
surface used here (register_worker, register_function, trigger,
TriggerRequest) is stable between the two.

Future work (out of scope this PR)

  • Engine change to emit registration deltas on the
    introspection.registrations pubsub channel so stream::subscribe
    can switch from snapshot to live tail.
  • A small skill (loaded via the skills worker) telling the agent to
    prefer introspection::functions::list over
    engine::functions::list. Separate PR.

Test plan

  • cargo build --release clean
  • Three integration tests in tests/integration.rs pass
  • Manual smoke: iii trigger --function-id introspection::context::bootstrap returns the expected frame against a running harness graph
  • CI pr-checks green (new worker — version > base trivially)
  • Lint clean (cargo fmt + cargo clippy)

Adds a new `introspection` worker that wraps `engine::workers::list` and
`engine::functions::list` with progressive disclosure. Default outputs are
2-3 KB instead of 50+ KB, with full schemas fetched only on demand.

Function ids registered:

  introspection::context::bootstrap
      One-shot session-start frame: connected workers, engine-builtins
      that are not enabled (with activation hints — e.g. iii-sandbox),
      root skill index ids only, canonical discovery flow, pinned tips.
      ~3 KB instead of the usual 50+ KB skill-bodies dump.

  introspection::context::worker_status
      Probe one worker for { status, builtin, activation_hint }. Lets the
      agent check iii-sandbox / iii-http / iii-cron registration state
      before assuming the functions exist.

  introspection::workers::list
      Slim worker list with { name, status, function_count, description }.
      Pass include=full for the raw graph. Filters anonymous host:port
      registrations and dedupes ghost reconnects.

  introspection::workers::describe
      Full detail for one worker, with embedded function entries slimmed
      (id + description only, no schemas) and associated root skills
      attached. ~2 KB instead of 30 KB raw slice.

  introspection::functions::list
      Slim function list { id, worker, description } across the bus.
      Drops 16 noise prefixes by default (skills::resources-*,
      engine::console::, telemetry::, hook-fanout::, policy-denylist::,
      auth::, introspection::stream::, etc). Pass include_noise: true to
      opt back in. Returns count + filtered_out for transparency.

  introspection::functions::describe
      Just-in-time full schema for one function id (request_format +
      response_format). Agent calls this only after picking an id from
      the slim list.

  introspection::stream::subscribe
      Snapshot of current registrations. Live deltas land when the
      engine emits on the introspection.registrations pubsub channel.

  introspection::registry::query
      HTTP GET workers.iii.dev/registry/index.json + substring filter.

Why this matters

Upstream guidance currently says "call engine::functions::list first".
The reply for a typical stack is ~52 KB of JSON (every function's full
request/response schema), and it sits in the transcript and is replayed
on every subsequent turn. With introspection a typical bootstrap +
keyword filter + one describe totals ~5 KB.

Architecture

Pure proxy over engine::workers::list + skills::list — no new state, no
new database, no engine changes. Each function is a thin reshape of an
existing engine query. Tests cover the slim entry shape, dedup, builtin
hint mapping, and registry-query filter.

Files

  introspection/Cargo.toml              iii-sdk =0.11.6
  introspection/iii.worker.yaml         iii: v1, binary deploy
  introspection/skill.md                top-level iii://introspection
  introspection/README.md               function table + flow
  introspection/config.yaml             registry_url + timeout
  introspection/src/main.rs             register the 8 functions
  introspection/src/config.rs           yaml load with defaults
  introspection/src/functions/mod.rs    DEFAULT_EXCLUDED_NAMESPACES,
                                        ENGINE_BUILTINS, shared helpers
  introspection/src/functions/context.rs
  introspection/src/functions/workers.rs
  introspection/src/functions/functions_mod.rs
  introspection/src/functions/stream.rs
  introspection/src/functions/registry.rs
  introspection/tests/integration.rs    slim entry + payload shape tests

Build verified locally against iii-sdk =0.11.6. To pin to upstream
=0.11.3 just bump the Cargo.toml line; the SDK surface used here
(register_worker, register_function, trigger, TriggerRequest) is stable
between the two.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Warning

Rate limit exceeded

@rohitg00 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 28 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8b3e8126-2c3f-4948-9e7b-7c35e5d014b9

📥 Commits

Reviewing files that changed from the base of the PR and between e198fbc and a73bdf3.

📒 Files selected for processing (8)
  • introspection/README.md
  • introspection/src/config.rs
  • introspection/src/functions/context.rs
  • introspection/src/functions/functions_mod.rs
  • introspection/src/functions/mod.rs
  • introspection/src/functions/registry.rs
  • introspection/src/functions/stream.rs
  • introspection/src/main.rs
📝 Walkthrough

Walkthrough

This PR adds a new iii-introspection worker crate that provides progressive-disclosure introspection endpoints. The worker enables agents to discover live workers, functions, and registry entries on a running engine without loading full schemas into context, using a series of slim-by-default responses that progressively enrich with detail on demand.

Changes

iii-introspection Worker

Layer / File(s) Summary
Package Definition & Purpose
introspection/Cargo.toml, introspection/iii.worker.yaml, introspection/skill.md, introspection/README.md
New Rust binary crate for iii-introspection with dependencies pinned to iii-sdk 0.11.6, configured for Tokio async runtime, CLI parsing via clap, HTTP client via reqwest (JSON + Rustls), and YAML config. Worker manifest declares binary deployment with no dependencies. Documentation describes progressive-disclosure approach, function catalog, build/run instructions, and anti-patterns.
Configuration Loading
introspection/src/config.rs
Introduces Config struct with registry_url and default_timeout_ms fields (deserialized from YAML with defaults), Default implementation, and load(path) function that validates file existence, deserializes YAML, and falls back to defaults on parse failure.
Shared Module Structure & Helpers
introspection/src/functions/mod.rs
Declares module structure for context, functions_mod, registry, stream, and workers submodules. Exports DEFAULT_EXCLUDED_NAMESPACES (prefixes to filter noisy functions), ENGINE_BUILTINS (mapping of builtin worker names to hints), and helpers call (10-second timeout wrapper), is_excluded (namespace prefix matching), and builtin_hint (static hint lookup).
Server Orchestration & Wiring
introspection/src/main.rs
Main CLI entrypoint with configurable --config (default ./config.yaml) and --url (default ws://127.0.0.1:49134). Initializes tracing, loads config, connects to iii engine, registers six introspection endpoints (introspection::workers::*, introspection::functions::*, introspection::stream::subscribe, introspection::context::*, introspection::registry::query) via closures, logs startup, and blocks on Ctrl+C. Includes private register_fn helper that wraps handlers into boxed futures and sends RegisterFunctionMessage with request schema and MCP expose metadata.
Context & Discovery: Bootstrap & Worker Status
introspection/src/functions/context.rs
Implements bootstrap endpoint that queries engine::workers::list, filters connected non-anonymous workers, deduplicates by name, builds a sorted connected_workers list, computes engine_builtins_disabled (missing builtins with hints), extracts root-only skill_index from skills::list, and returns combined JSON with pinned tips and discovery flow. Also implements worker_status that validates payload.name, looks up that worker, derives status (default not_registered), and returns builtin metadata.
Workers Introspection: List & Describe
introspection/src/functions/workers.rs
Implements list that fetches workers from engine::workers::list, applies optional name/connection filtering, optionally reshapes to slim format ({name, status, function_count, description}), annotates builtin hints, and returns include mode, count, builtin names, and processed workers. Implements describe that validates required name, locates matching worker, returns either builtin-hint payload or not-found error when absent, slims embedded functions to {id, description} while filtering excluded ids, annotates builtins, and best-effort attaches related skills from skills::list matching worker name or prefix.
Functions Introspection: List & Describe
introspection/src/functions/functions_mod.rs
Implements list that extracts optional filters (worker, filter substring, include_noise, exclude_prefixes), calls engine::workers::list, iterates connected workers and their functions, derives ids and optional descriptions, applies exclusion and matching rules, tracks filtered_out, and returns count, filtered count, default prefixes, and functions array. Implements describe that requires payload.id, scans worker functions, returns found function with injected worker field or minimal object, errors with function not found if no match.
Streaming Subscriptions & Registry Query
introspection/src/functions/stream.rs, introspection/src/functions/registry.rs
subscribe extracts since_ms from payload, fetches worker list via engine, converts to snapshot events with kind, worker, status, function_count, and returns JSON with channel name and snapshot-only note. query requires payload.q, reads optional limit (default 20), fetches registry/index.json from cfg.registry_url, filters entries by case-insensitive substring match against name/description, truncates to limit, and returns query, registry URL, match count, and results.
Integration Tests & Validation
introspection/tests/integration.rs
Three test assertions validate stable JSON shape: slim worker entry with string fields and numeric function_count, slim function entry with string fields and numeric limit, and registry query payload with required q field and matching limit value.

Sequence Diagram(s)

No sequence diagram needed for this change; the PR introduces a new worker with multiple independent endpoints, each serving different introspection queries without complex cross-endpoint orchestration or state machines.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • sergiofilhowz

Poem

🐰 A whisker-twitching discovery,
Progressive disclosure with care,
Workers and functions laid bare,
No heavy schemas to bear—
Just what you need, when you need it there!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely describes the main addition: a new introspection worker with slim, just-in-time design to progressively disclose engine introspection data.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/introspection-worker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@introspection/src/config.rs`:
- Line 36: The YAML parsing currently uses
serde_yaml::from_str(&text).unwrap_or_default() which swallows parse errors;
change this to propagate the parse error instead (e.g., return Err(...) or use
the ? operator) so callers receive the failure instead of receiving a default
Config; update the code around the Config binding (the cfg variable) and the
enclosing function signature to return a Result<Config, serde_yaml::Error> (or
the crate's common error type) and remove unwrap_or_default(), ensuring
serde_yaml::from_str(&text) errors flow back to the caller.

In `@introspection/src/functions/context.rs`:
- Around line 173-179: The current code uses workers.iter().find(...) which
returns the first matching row and can yield stale statuses; change the lookup
to pick the most recent matching entry by using workers.iter().rfind(|w|
w.get("name").and_then(|n| n.as_str()) == Some(name)) (i.e., replace .find()
with .rfind()) so row reflects the latest duplicate entry, leaving the
subsequent status extraction (the status variable and its unwrap_or fallback)
unchanged.

In `@introspection/src/functions/functions_mod.rs`:
- Around line 110-123: The describe() loop currently matches fn_id across all
workers including disconnected ones; restrict it to connected workers by
checking the worker status before iterating functions—e.g., add a guard like if
w.get("status").and_then(|s| s.as_str()) != Some("connected") { continue } (or
the equivalent status field your codebase uses) immediately after obtaining
worker_name so that only workers with status "connected" are considered when
matching fn_id (variables to update: workers loop, worker_name, fns, fn_id, id).

In `@introspection/src/functions/registry.rs`:
- Around line 19-29: The code currently calls resp.json() without checking HTTP
status, so replace that flow in registry.rs (inside the function that builds
`resp`) by first calling resp.error_for_status() (or error_for_status_ref()) and
map any status error into an IIIError::Handler with the URL and status/error
details; only after a successful status call, proceed to deserialize with
resp.json(). Ensure the error mapping includes the HTTP status (and optionally
response body/text) to give accurate diagnostics instead of a misleading "json
parse failed" message.

In `@introspection/src/functions/stream.rs`:
- Around line 24-27: The mapped "function_count" currently defaults to json!(0)
which is misleading when the worker object `w` contains a `functions` array but
no `function_count`; change the mapping logic in
introspection/src/functions/stream.rs so that for each worker `w` you first try
`w.get("function_count")` and if absent compute the count from
`w.get("functions")` by taking its array length (e.g., map to
json!(functions_array.len()) when `functions` is present and an array), and only
fall back to json!(0) if neither exists; update the expression that builds the
worker map (where "function_count" is set) to perform this conditional
lookup/derive using `w.get("functions")` and `as_array()`/len to produce the
correct count.

In `@introspection/src/functions/workers.rs`:
- Around line 23-41: Normalize and de-duplicate the `workers` collection before
applying the `filter` and `include_disconnected` logic: replace the current
`workers` Vec manipulation with a deterministic normalization step that collects
rows into a map keyed by worker "name" (symbol: workers), resolving duplicates
by preferring a connected row (status == "connected") or the most recent row
(e.g., compare a "last_seen" / timestamp field if present), then rebuild a
deduped Vec from the map and sort it deterministically (e.g., by name or
last_seen) before the existing filter/retain code runs; apply the same
normalization/dedupe routine to the other usage of `workers` referenced in the
same file (the block around the other instance noted in the review) so both
`list` and `describe` operate on the same cleaned dataset.

In `@introspection/src/main.rs`:
- Line 188: The startup log string is hardcoded to "registered 6 functions" but
the process actually registers 8 functions; update the tracing::info! call (the
line with tracing::info!("iii-introspection registered 6 functions, awaiting
calls")) to report the correct count — either change the literal to "8" or,
better, compute and interpolate the actual number from the collection or
variables used when registering functions (e.g., use the length of the Vec or
slice that holds registrations) so the message always reflects the true number
of registered functions.
- Line 31: The current call uses config::load(&cli.config).unwrap_or_default(),
which hides load/parse errors; change it to fail fast by handling the Result
from config::load(&cli.config) instead of defaulting: if load returns Err, log
or print the error (including the Err value) and exit with a non-zero code;
otherwise wrap the Ok value in Arc and assign to cfg. Update the expression
around config::load, unwrap_or_default, and cfg to perform this explicit error
handling (e.g., using match, unwrap_or_else, or ? propagated to main) so
failures are not silently masked.

In `@introspection/tests/integration.rs`:
- Around line 3-34: Tests currently assert against hardcoded json! literals (in
slim_worker_entry_shape_is_stable, slim_function_entry_shape_is_stable, and
registry_query_payload_validates_required_fields) so they don't exercise
production code; replace the hardcoded json! values with calls to the actual
production shaping/handler functions that create those payloads (or to a shared
helper used by the handlers) and assert on the returned serde_json::Value
instead. Concretely: import and call the real functions used by the service to
build slim worker entries, slim function entries, and registry query payloads
(replace the json!({ ... }) assignments in slim_worker_entry_shape_is_stable,
slim_function_entry_shape_is_stable, and
registry_query_payload_validates_required_fields with those function calls),
then keep the same assertions against the returned Value. Ensure async test uses
the async handler if needed and add any necessary feature/imports to access the
production functions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2764a784-6ded-40fe-9bdc-1d181074cbb9

📥 Commits

Reviewing files that changed from the base of the PR and between cca571f and e198fbc.

⛔ Files ignored due to path filters (1)
  • introspection/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • introspection/Cargo.toml
  • introspection/README.md
  • introspection/iii.worker.yaml
  • introspection/skill.md
  • introspection/src/config.rs
  • introspection/src/functions/context.rs
  • introspection/src/functions/functions_mod.rs
  • introspection/src/functions/mod.rs
  • introspection/src/functions/registry.rs
  • introspection/src/functions/stream.rs
  • introspection/src/functions/workers.rs
  • introspection/src/main.rs
  • introspection/tests/integration.rs

Comment thread introspection/src/config.rs Outdated
Comment thread introspection/src/functions/context.rs Outdated
Comment thread introspection/src/functions/functions_mod.rs
Comment thread introspection/src/functions/registry.rs
Comment thread introspection/src/functions/stream.rs
Comment thread introspection/src/functions/workers.rs
Comment thread introspection/src/main.rs Outdated
Comment thread introspection/src/main.rs Outdated
Comment on lines +3 to +34
#[test]
fn slim_worker_entry_shape_is_stable() {
let entry = json!({
"name": "shell-bash",
"status": "connected",
"function_count": 3,
"description": "Sandboxed bash exec",
});
assert_eq!(entry["name"], "shell-bash");
assert_eq!(entry["status"], "connected");
assert_eq!(entry["function_count"], 3);
assert!(entry["description"].is_string());
}

#[test]
fn slim_function_entry_shape_is_stable() {
let entry = json!({
"id": "shell::bash::exec",
"worker": "shell-bash",
"description": "Run bash in a sandbox",
});
assert_eq!(entry["id"], "shell::bash::exec");
assert_eq!(entry["worker"], "shell-bash");
assert!(entry["description"].is_string());
}

#[tokio::test]
async fn registry_query_payload_validates_required_fields() {
let payload = json!({"q": "mcp", "limit": 5});
assert!(payload.get("q").is_some());
assert_eq!(payload["limit"], 5);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Current “integration” tests don’t exercise production code paths

These tests validate hardcoded JSON literals built inside the test itself, so they pass even if the handler implementations break. Please wire them to actual handler outputs (or shared shaping functions) so they protect real behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@introspection/tests/integration.rs` around lines 3 - 34, Tests currently
assert against hardcoded json! literals (in slim_worker_entry_shape_is_stable,
slim_function_entry_shape_is_stable, and
registry_query_payload_validates_required_fields) so they don't exercise
production code; replace the hardcoded json! values with calls to the actual
production shaping/handler functions that create those payloads (or to a shared
helper used by the handlers) and assert on the returned serde_json::Value
instead. Concretely: import and call the real functions used by the service to
build slim worker entries, slim function entries, and registry query payloads
(replace the json!({ ... }) assignments in slim_worker_entry_shape_is_stable,
slim_function_entry_shape_is_stable, and
registry_query_payload_validates_required_fields with those function calls),
then keep the same assertions against the returned Value. Ensure async test uses
the async handler if needed and add any necessary feature/imports to access the
production functions.

rohitg00 added 6 commits May 11, 2026 17:25
- config.rs: propagate yaml parse errors instead of silently defaulting
- main.rs: fail fast on malformed config; only fall back on missing file
- main.rs: fix startup log function count (6 -> 8)
- registry.rs: explicit HTTP status check + content-type check before
  JSON deserialization; clearer error on non-2xx and on HTML/text bodies
- stream.rs: derive function_count from functions[] when source omits
  the explicit field, via new effective_fn_count helper
- functions/mod.rs: new shared helpers dedup_workers, effective_fn_count,
  is_anonymous_name. dedup drops anonymous host:port rows, prefers
  status=connected, then highest function_count
- functions_mod.rs (describe): dedup + restrict to connected workers
  so describe never returns stale metadata from a ghost row
- context.rs (worker_status): dedup before .find() to avoid picking a
  stale row over a live one
- 5 new unit tests in functions/mod.rs covering anon detection, fn
  count fallback, exclusion prefixes, dedup ordering, builtin hints
@rohitg00 rohitg00 requested review from sergiofilhowz and ytallo May 11, 2026 17:30
@rohitg00 rohitg00 marked this pull request as draft May 14, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant