Skip to content

feat(provider-tangle): observation and interactive-terminal wiring (#136 phase 2) - #171

Merged
drewstone merged 3 commits into
mainfrom
feat/136-tangle-observation-terminal
Aug 14, 2026
Merged

feat(provider-tangle): observation and interactive-terminal wiring (#136 phase 2)#171
drewstone merged 3 commits into
mainfrom
feat/136-tangle-observation-terminal

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

What

Phase 2 of #136: the provider side of the observation + interactive-terminal contract that shipped in #158.

  • Tangle observation — provider/session identity, lifecycle, credential-free endpoint, requested + verified placement, resources and resource use, per-execution usage and cost, account usage. Every field that the SDK cannot back reports its freshness state (unavailable / unknown) rather than a zero, so a missing number is always visibly missing.
  • Interactive terminal — attach lifecycle, ordered output with a replay cursor, input, resize, detach/close, parent-execution identity, fail-closed expiry, over the already-live sandbox transport.
  • cli-bridge — usage/placement readout narrowed to what the bridge actually proves.

Security fix included

describeFailure() copied the raw transport error into the observation payload on five paths. The Sandbox SDK throws <url> failed (<status>): <message>, so a URL carrying userinfo or a token landed in an observation for which the contract's own observationContainsCredential() returned true. Reasons are now bounded and credential-free, asserted on all five paths.

Verification

Two adversarial review rounds. The final review found no functional defect and independently mutation-proved all eight closures, including:

  • the credential-free reason on every failing path;
  • the cli-bridge narrowing (proved by an over-claiming caller-supplied document, not a literal on already-false defaults);
  • per-field degradation, so one out-of-bounds value (plan: 'pro ') can no longer destroy an entire observation;
  • detach making a terminal ref genuinely unusable;
  • a frame-buffer cursor a fresh consumer can always read after the 1024-frame buffer wraps.

Suites green across provider-tangle and cli-bridge; pnpm -r check-types, check:package-artifacts, check:control-artifacts all pass.

Known test gap (not a defect)

close()'s registry release is correct but unpinned — deleting the release() call survives the suite. Worth a follow-up pin; it does not change behavior.

Refs #136

…andbox and bridge transports (#136)

The Tangle provider implements the normalized environment observation over the
Sandbox SDK: provider identity, lifecycle with its scheduled retirement, the
credential-free runtime endpoint, verified placement, requested and effective
compute shape, current and peak memory use, per-execution token usage and cost,
GPU-lease compute billing, and the account plan, credits, quota, and period.
Each surface carries its freshness state, so a value the SDK does not report is
visibly absent instead of arriving as a measured zero.

Sandbox states no effective CPU or disk, no CPU utilization, and no per-sandbox
container compute cost, so those surfaces report unavailable with the reason. A
negative overage credit balance and an account with no concurrent-sandbox
ceiling report unavailable for the same reason.

The Tangle provider also binds the interactive terminal over the sandbox PTY
socket: attach and reattach, ordered output with an exclusive replay cursor,
input, resize, detach, and close, each bound to its parent execution and to a
fail-closed expiry one detach window past the newest proven activity. Close
reports closed only when the socket delivered an exit.

Both capability blocks are claimed only where a concrete source backs them.

The create call now sends the compute shape Sandbox reads (cpuCores, memoryMB,
diskGB, accelerator). The contract states cpu, memoryMb, and diskMb, and passing
those names through unchanged reached the service as unknown fields, so a
requested size was never applied.

The cli-bridge provider reports the same observation for the surfaces it backs:
identity, lifecycle, the bridge endpoint, placement, and the token usage of the
newest execution, summed across that run's events. It provisions no compute and
holds no account, so those surfaces are never claimed.
…nd a terminal to its socket

A Sandbox transport failure states the request URL in its message, and that URL
can carry userinfo or a token, so no observation or terminal attach result
repeats it. Each degraded surface names the read that failed and a structured
cause: an HTTP status, an error code, an abort, or an error name.

Every observation surface is held to the schema the contract states for it, and
an SDK accessor that throws is read through the same guard, so one refused value
degrades that surface alone instead of destroying the observation beside it.

The observation subject names only the provider and the environment id, which
both a create handle and a handle rebuilt by id can produce, so a live
observation binds to its replay for the same sandbox.

A terminal reference describes what its own socket can do: a detached, closed,
or dropped socket makes the reference unusable, and a reattach closes the socket
it replaces. The terminal states the replay cursors it can serve, and the oldest
one delivers every retained frame, so eviction cannot lock a consumer out.

cli-bridge carries its configured execution kind as the requested placement and
states the verified placement and a live lifecycle as unavailable, because it
never probes the bridge.
…wledgement read, and release the terminal registry

`events()` with no cursor starts at the oldest retained frame, resolved when
the read begins. Cursor 0 is the one value the bounded log refuses after it
evicts an output frame, so a consumer that held no cursor was locked out. A
cursor the consumer names is still refused when its successors were evicted.

`attachTerminal()` reads the ready acknowledgement inside a guard and closes
the socket it opened. The Sandbox stream exposes `ready` as an accessor that
throws before the runtime acknowledges, so an unguarded read replaced the
attach result with a raw transport error that also states the request URL.

A terminal handle releases its registry entry on detach and on close, matched
by the socket the handle was built on, so a stale handle cannot evict the
terminal a later attach installed.
@drewstone
drewstone merged commit 18dd3ce into main Aug 14, 2026
1 check passed

@tangletools tangletools left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Auto-approved drewstone PR — bf0d7e86

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

This approval is provisional. It rests on the audit running. If the audit cannot run — for example the CLI bridge rejects it — this approval is dismissed rather than left standing, so an unrun check never reads as a passing one.

tangletools · auto-approval · reason: drewstone_author · 2026-08-14T06:31:58Z

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.

2 participants