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
2 changes: 1 addition & 1 deletion docs/plugins/file-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ first-party file-manager package.
Pre-implementation ecosystem: the plugin package, manifest, and policy are
implemented and tested headlessly, while the Bitty host is still landing the
host-mediated filesystem bridge. Panel presentation stays deferred pending the
panel-provider contract (`bitty-docs` `CTX-0181`, OQ-058). `:rename` is
panel-provider contract (`bitty-docs` `CTX-0181`, OQ-058; accepted contract text in RFC-0006, `bitty-docs#443` OPEN/`#445` merged). `:rename` is
policy-only composition with no host filesystem mutation; host-side
rename/move/copy stays deferred pending the `fs.write:PATTERN` grant,
consistent with the file-manager candidate in the Plugin Matrix. The package
Expand Down
2 changes: 1 addition & 1 deletion docs/plugins/git-panel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ first-party git-panel package.

Pre-implementation ecosystem: the plugin package, manifest, and policy are
implemented and tested headlessly, while the Bitty host is still landing the
Layer 2 spawn bridge and the panel-provider contract. The package is not
Layer 2 spawn bridge and the panel-provider contract (predecessor: RFC-0006, `bitty-docs#443` OPEN/`#445` merged; presentation stays deferred). The package is not
verified, compatible, or shipped beyond its manifest `[compat]` ranges.

## Pages
Expand Down
4 changes: 2 additions & 2 deletions product/plugin-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ without converging the two surfaces.
> disagree, the decision record is authoritative for the verdict and its gate.
> OQ-053 is closed by that record (2026-09-14); the residual panel-provider
> gate it cites stays open as
> [OQ-058](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/open-questions.md).
> [OQ-058](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/open-questions.md), with accepted contract text in RFC-0006 (`bitty-docs#443` OPEN/`#445` merged).

Status: **candidate, non-normative**. Bundled-disabled and independent are
distribution states, not privilege tiers: an independent first-party plugin
Expand Down Expand Up @@ -306,7 +306,7 @@ Candidate invariants if this migration is accepted:

Closed as [OQ-053](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/open-questions.md)
by the decision record on 2026-09-14; the residual panel-provider gate is
tracked as [OQ-058](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/open-questions.md).
tracked as [OQ-058](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/open-questions.md) with accepted contract text in RFC-0006 (`bitty-docs#443` OPEN/`#445` merged).

### Bundled-plugin suitability rules (candidate)

Expand Down
54 changes: 24 additions & 30 deletions sdk/reference/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ sidebar_order: 110
> [Plugin API v1 Lua Surface RFC](../plugin-api-v1-lua-surface-rfc.md) and
> [ADR 0006](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/adrs/ADR-0006-os-env-policy.md);
> executable behavior lives in the `bitty` repository
> (`crates/bitty-lua/src/host.rs`, `bitty@7da6d6f`). This page states no
> (`crates/bitty-lua/src/host.rs`, `bitty@c2aaf218`). This page states no
> behavior beyond what those sources pin.

## Purpose and scope
Expand All @@ -25,7 +25,7 @@ environment variables without ambient OS authority (the RFC references the
ADR 0006 contract instead of redefining it). Its current status is
`not_implemented`: the `bitty` route table marks it `not_implemented`
([Lua API Reference](README.md)), and every call fails closed until a host
backend lands (`crates/bitty-lua/src/host.rs`, `bitty@7da6d6f`). Do not
backend lands (`crates/bitty-lua/src/host.rs`, `bitty@c2aaf218`). Do not
treat its functions as usable.

## Signature
Expand All @@ -34,7 +34,7 @@ There are no implemented Lua entry points in this namespace. The accepted
RFC vocabulary names `bitty.env.get` / `bitty.env.has`
(RFC "Notifications and environment"), but neither is callable behavior:
the `HostServices` trait defaults fail closed with `E_NOT_IMPLEMENTED`
(`crates/bitty-lua/src/host.rs`, `bitty@7da6d6f`) — `env_get` at
(`crates/bitty-lua/src/host.rs`, `bitty@c2aaf218`) — `env_get` at
`host.rs:533` (`Err(BridgeError::not_implemented("bitty.env.get"))`) and
`env_has` at `host.rs:544`
(`Err(BridgeError::not_implemented("bitty.env.has"))`), both constructed by
Expand All @@ -44,11 +44,11 @@ the `HostServices` trait defaults fail closed with `E_NOT_IMPLEMENTED`

The following describes the expected contract shapes only, as derived from
the Rust side — not callable behavior. The key argument is checked against
the **host Lua-call key-shape bound**: shape `[A-Za-z_][A-Za-z0-9_]*` of
`1..128` bytes (`ENV_KEY_MAX_BYTES = 128`, `host.rs:86`;
`crates/bitty-lua/src/host.rs` `env_get` docs, `bitty@7da6d6f`). An
over-bound key is rejected fail-closed with `E_DEF_LIMIT` before any grant
check (`host.rs:81-86`). A shape-invalid key is rejected with `E_DEF_INVALID`
the **host Lua-call key-shape bound**: shape `^[A-Z_][A-Z0-9_]*$` of
`1..64` bytes (`ENV_KEY_MAX_BYTES = 64`;
`crates/bitty-lua/src/host.rs` `validate_env_key` docs, `bitty@c2aaf218`). An
over-bound or shape-invalid key is rejected fail-closed with `E_ENV_KEY_INVALID` before any grant
check. Malformed keys are rejected
(pinned by `env_bridge_rejects_malformed_keys_before_grants` in
`crates/bitty-lua/tests/lua_parity.rs`). This runtime bound is not the
manifest grant key bound; the two bounds and the layer each one applies to
Expand All @@ -59,29 +59,29 @@ are separated in [Capability](#capability).
The following describes the expected contract shapes only, as derived from
the Rust side — not callable behavior. A granted-but-absent key resolves
to `Ok(None)`, i.e. Lua `nil` (`crates/bitty-lua/src/host.rs` `env_get`
docs, `bitty@7da6d6f`). With a valid grant, a read for a non-allowlisted
docs, `bitty@c2aaf218`). With a valid grant, a read for a non-allowlisted
key returns `nil`, indistinguishable from an unset variable (RFC
"Notifications and environment").

## Errors

Denials arrive as catchable Lua error tables (`class` / `code` / `message`,
per `BridgeError::to_error` in `crates/bitty-lua/src/host.rs`,
`bitty@7da6d6f`); match on `code`.
`bitty@c2aaf218`); match on `code`.

| `code` | `class` | When |
| ------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `E_NOT_IMPLEMENTED` | `runtime` | Every call on the default host: no env backend is wired (`host.rs:533`, `host.rs:544`). An ungranted key shares this same code, so callers cannot probe which keys exist (`host.rs` `env_get` docs; `env_bridge_denies_ungranted_keys_without_leak` in `crates/bitty-lua/tests/lua_parity.rs`). |
| `E_DEF_INVALID` | `validation` | Shape-invalid key at the bridge (caller shape above; `env_bridge_rejects_malformed_keys_before_grants` in `crates/bitty-lua/tests/lua_parity.rs`). |
| `E_DEF_LIMIT` | `validation` | Over-bound key (`> 128` bytes) at the bridge, before any grant check (`host.rs:81-86`). |
| `E_ENV_KEY_INVALID` | `validation` | Shape-invalid or over-bound key at the bridge (caller shape above; `env_bridge_rejects_malformed_keys_before_grants` in `crates/bitty-lua/tests/lua_parity.rs`). |
| `E_ENV_KEY_INVALID` | `validation` | Over-bound key (`> 64` bytes) at the bridge, before any grant check (`validate_env_key`). |

## Capability

`CapabilityFamily::Env` (`crates/bitty-plugin-host/src/capability.rs`,
`Env` variant: host-mediated environment reads, `env.read:<KEY>`). The
expected gate is one grant per key: the trait docs require an
`env.read:<KEY>` grant for `key`
(`crates/bitty-lua/src/host.rs` `env_get` docs, `bitty@7da6d6f`), and the
(`crates/bitty-lua/src/host.rs` `env_get` docs, `bitty@c2aaf218`), and the
RFC extension-level split lists the surface as gated by `env.read:<KEY>`
(RFC extension table). The canonical manifest spelling is owned by the
accepted
Expand Down Expand Up @@ -113,24 +113,18 @@ neither one implies the other:
| Bound | Layer it applies to | Value and enforcement |
| ----------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Manifest grant key bound | the `env.read:<KEY>` parameter a manifest declares | 64 bytes; uppercase ASCII key (`^[A-Z_][A-Z0-9_]*$`, length 1..64) per [ADR 0006](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/adrs/ADR-0006-os-env-policy.md), applied by the SDK manifest validator (`bitty-plugin-sdk` `src/capabilities.ts`, `MAX_ENV_KEY_LEN = 64`) |
| Host Lua-call key-shape bound | the `key` argument of `bitty.env.get` / `bitty.env.has` | 128 bytes; shape `[A-Za-z_][A-Za-z0-9_]*`, length `1..128` (`crates/bitty-lua/src/host.rs`, `ENV_KEY_MAX_BYTES = 128`, `bitty@7da6d6f`), denied fail-closed with `E_DEF_LIMIT` before any grant check |
| Host Lua-call key-shape bound | the `key` argument of `bitty.env.get` / `bitty.env.has` | 64 bytes; shape `^[A-Z_][A-Z0-9_]*$`, length `1..64` (`crates/bitty-lua/src/host.rs`, `ENV_KEY_MAX_BYTES = 64`, `bitty@c2aaf218`), denied fail-closed with `E_ENV_KEY_INVALID` before any grant check |

A manifest grant is therefore bounded more tightly than a runtime call
argument: a 64-byte `env.read:<KEY>` declaration is the longest one the SDK
validator accepts, while the host bridge still accepts a call argument up to
128 bytes before it consults the allowlist. Passing a longer key at run time
A manifest grant and a runtime call argument share the same bound: a 64-byte `env.read:<KEY>` declaration is the longest one the SDK
validator accepts, and the host bridge accepts a call argument up to
64 bytes before it consults the allowlist. Passing a longer key at run time
never widens a grant, and a declared grant never raises the runtime shape
bound.

One point of the two layers disagrees, and this page records the
disagreement instead of choosing silently: the accepted
The two layers agree following `bitty#1751` via `#1752` (CTX-1004): the accepted
[ADR 0006](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/adrs/ADR-0006-os-env-policy.md)
contract text bounds the `bitty.env` `name` argument to the same
`^[A-Z_][A-Z0-9_]*$` 1..64 key and names its denial `E_ENV_KEY_INVALID`,
while the pinned host revision accepts the wider `1..128`
`[A-Za-z_][A-Za-z0-9_]*` shape and denies with `E_DEF_INVALID` /
`E_DEF_LIMIT`. Reconciling the accepted ADR text with the host implementation
is an owner decision, not a documentation choice.
contract bounds the `bitty.env` key to `^[A-Z_][A-Z0-9_]*$` 1..64 with denial `E_ENV_KEY_INVALID`,
and the host bridge enforces the same bound (`ENV_KEY_MAX_BYTES = 64`, `bitty@c2aaf218`). The prior wider host shape is retired.

## Example

Expand All @@ -151,9 +145,9 @@ granted-path claim beyond this denial shape is follow-up work.
- Not implemented: this namespace has no callable signatures, and every
statement above about params, returns, and gates describes the expected
contract only. All of it is subject to change when a host backend lands.
- Key-shape checks (`E_DEF_INVALID` / `E_DEF_LIMIT`) run before any grant
check; oversize input never reaches the allowlist (`host.rs:81-86`). This is
the 128-byte runtime bound; the 64-byte manifest grant bound is enforced
- Key-shape checks (`E_ENV_KEY_INVALID`) run before any grant
check; oversize input never reaches the allowlist (`validate_env_key`). This is
the 64-byte runtime bound; the 64-byte manifest grant bound is enforced
earlier, by the SDK manifest validator ([Capability](#capability)).
- Ungranted keys are deliberately indistinguishable from unimplemented ones
(same `E_NOT_IMPLEMENTED` code), so key presence cannot be probed
Expand Down Expand Up @@ -192,7 +186,7 @@ runtime `bitty.api_version`.
`not_implemented`)
- `bitty` `crates/bitty-lua/src/host.rs` (`BridgeError::not_implemented`
at `host.rs:444`; `env_get` default at `host.rs:533`; `env_has` default
at `host.rs:544`; `ENV_KEY_MAX_BYTES = 128` at `host.rs:86`)
at `host.rs:544`; `ENV_KEY_MAX_BYTES = 64` (`validate_env_key`))
- `bitty` `crates/bitty-plugin-host/src/capability.rs`
(`CapabilityFamily::Env`, `env.read` closed identifier)
- `bitty` `crates/bitty-lua/tests/lua_parity.rs`
Expand Down
Loading