Skip to content

Commit e61f29a

Browse files
authored
Unify admin workbench UI (#64)
* Add Gleam state machine subsystem spike * Integrate Lustre workbench with Gleam projections * Add Lustre state graph entrypoint * Add graph renderer comparison lab * Render Lustre state graphs with Cytoscape * Improve Lustre graph replay and retry inputs * Add model-driven Lustre demo graphs * Improve Lustre graph model boundaries * Promote workbench route and labels * Clarify workbench controls * Fix workbench fixture controls * Port model access UI to Lustre * Preserve model access deep links * Fix Gleam dev reloading * Test Gleam dev reload wiring * Review map boundary baseline for workbench * Satisfy workbench type checks * Unify admin workbench UI * Prepare v0.0.9 release * Harden UI docs guard * Fix admin browser smoke target
1 parent 1bc892d commit e61f29a

66 files changed

Lines changed: 9833 additions & 625 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ jobs:
1818
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
1919
with:
2020
persist-credentials: false
21+
fetch-depth: 0
2122
- uses: erlef/setup-beam@8251c48667b97e88a0a24ec512f5b72a039fcea7 # v1
2223
with:
2324
otp-version: "29.0"
@@ -62,8 +63,11 @@ jobs:
6263
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
6364
with:
6465
persist-credentials: false
66+
fetch-depth: 0
6567
- name: Check docs links
6668
run: ruby scripts/check-docs-site.rb
69+
- name: Check UI docs acknowledgement
70+
run: scripts/check-ui-docs-ack.sh
6771

6872
test:
6973
name: test

README.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ decision happened.
1313
## Install
1414

1515
Wardwright publishes early native binaries for macOS and Linux. The latest
16-
published release is `v0.0.8`, with a model-aware workbench, saved simulator
16+
published release is `v0.0.9`, with a model-aware workbench, saved simulator
1717
test cases, an experimental in-page authoring assistant, and local ratchets for
1818
style and browser-regression checks.
1919

@@ -45,14 +45,14 @@ WARDWRIGHT_BIND=127.0.0.1:8787 \
4545
For a pinned release:
4646

4747
```bash
48-
curl -fsSL https://raw.githubusercontent.com/bglusman/wardwright/main/scripts/install.sh | sh -s -- --version v0.0.8
48+
curl -fsSL https://raw.githubusercontent.com/bglusman/wardwright/main/scripts/install.sh | sh -s -- --version v0.0.9
4949
```
5050

5151
Set `WARDWRIGHT_ADMIN_TOKEN` before exposing Wardwright beyond loopback. See
5252
[Packaging](docs/packaging.md) for release targets, manual archive install
5353
steps, and service details.
5454

55-
Then visit `http://127.0.0.1:8787/policies`. Set `BASIC_AUTH_PASSWORD` before
55+
Then visit `http://127.0.0.1:8787/admin`. Set `BASIC_AUTH_PASSWORD` before
5656
exposing the workbench or protected control APIs beyond loopback; the Basic Auth
5757
username is always `admin`. Model calls remain governed separately by model
5858
access configuration.
@@ -69,8 +69,8 @@ model-scoped API key, or set unkeyed models to internal-only composition:
6969
}
7070
```
7171

72-
Use the protected `/admin/model-api-keys` page to select a registered model and
73-
generate or revoke keys for that model. Raw keys are shown once; Wardwright
72+
Use the protected `/admin` operator UI to select a registered model and generate
73+
or revoke keys for that model. Raw keys are shown once; Wardwright
7474
stores only a hash in the SQLite store at
7575
`~/.local/share/wardwright/wardwright.sqlite3` unless `XDG_DATA_HOME` or
7676
`WARDWRIGHT_SQLITE_STORE` points somewhere else. The same store persists
@@ -97,7 +97,8 @@ wardwright tools --json
9797
Wardwright exposes:
9898

9999
- OpenAI-compatible `/v1/chat/completions` and `/v1/models` endpoints.
100-
- A policy workbench at `/policies`.
100+
- A registered-model workbench at `/admin`, with the legacy policy
101+
projection workbench still available at `/policies`.
101102
- Protected authoring APIs, plus MCP tools at `/mcp`.
102103
- Receipts, simulations, model access details, and admin status endpoints.
103104

@@ -123,24 +124,25 @@ provider credentials on an instance reachable by untrusted users. See
123124

124125
## Policy Workbench
125126

126-
The installed service includes a LiveView workbench at `/policies`. It loads
127-
seeded and local examples, lets you choose the Wardwright model being simulated,
128-
edit caller input, backend model output, retry attempts, and relevant history,
129-
then steps through routing, state transitions, stream retries, rewrites, tool
130-
decisions, and receipt events. Reviewed turns can be saved as reusable test cases
131-
for later simulation, regression export, or agent review.
127+
The installed service includes a registered-model workbench at `/admin`. It
128+
lets you choose the Wardwright model being simulated, load a fixture, edit caller
129+
input, backend model output, and retry attempts, then step through routing,
130+
state transitions, stream retries, rewrites, tool decisions, and receipt events.
131+
The older `/policies` workbench remains available as a legacy fallback during
132+
the transition.
132133

133-
![Wardwright policy workbench showing context-window dispatcher simulation](docs/assets/workbench/route-composition-simulator.png)
134+
![Wardwright registered-model workbench showing a retry fixture](docs/assets/workbench/registered-model-workbench.png)
134135

135-
See [Policy Workbench](docs/workbench.md) for screenshots and the example
136-
catalog. See [Model Middleware](docs/wardwright-models.md) for the current model
136+
See [Policy Workbench](docs/workbench.md) for screenshots and workflow details.
137+
See [Model Middleware](docs/wardwright-models.md) for the current model
137138
composition shape.
138139

139140
## Current Runtime
140141

141-
The active app is a Phoenix/LiveView service. Elixir owns runtime plumbing,
142-
provider calls, HTTP/API boundaries, receipts, and the UI. Gleam is used for
143-
correctness-heavy pure policy logic where the boundary is stable.
142+
The active app is a Phoenix service with server-rendered operator workbenches.
143+
Elixir owns runtime plumbing, provider calls, HTTP/API boundaries, and receipts;
144+
Gleam is used for correctness-heavy pure policy logic where the boundary is
145+
stable.
144146

145147
Current capabilities include:
146148

app/config/dev.exs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
import Config
22

3+
code_reloader? = System.get_env("WARDWRIGHT_CODE_RELOADER", "true") != "false"
4+
gleam_code_reloader? = System.get_env("WARDWRIGHT_GLEAM_CODE_RELOADER", "false") == "true"
5+
6+
reloadable_compilers =
7+
if code_reloader? do
8+
[:elixir, :app] ++ if(gleam_code_reloader?, do: [:gleam, :erlang], else: [])
9+
else
10+
[]
11+
end
12+
13+
live_reload_patterns = [
14+
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
15+
~r"lib/wardwright_web/(controllers|live|components|layouts)/.*(ex|heex)$",
16+
~r"lib/wardwright_web/.*_controller.ex$",
17+
~r"lib/wardwright_web/.*_socket.ex$"
18+
]
19+
20+
live_reload_patterns =
21+
live_reload_patterns ++
22+
if(gleam_code_reloader?, do: [~r"src/wardwright/.*\.gleam$"], else: [])
23+
324
config :phoenix_live_view,
425
debug_attributes: true,
526
debug_heex_annotations: true
27+
28+
config :wardwright, WardwrightWeb.Endpoint,
29+
code_reloader: code_reloader?,
30+
debug_errors: true,
31+
reloadable_compilers: reloadable_compilers,
32+
live_reload: [
33+
patterns: live_reload_patterns
34+
]

app/gleam.toml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,43 @@ target = "erlang"
44

55
[dependencies]
66
gleam_stdlib = ">= 1.0.0 and < 2.0.0"
7+
act = ">= 0.4.0 and < 1.0.0"
8+
gleam_json = ">= 3.1.0 and < 4.0.0"
9+
lustre = ">= 5.7.0 and < 6.0.0"
10+
glizzy = ">= 0.1.0 and < 1.0.0"
11+
non_empty_list = ">= 2.3.0 and < 3.0.0"
12+
trie_again = ">= 1.1.4 and < 2.0.0"
713

814
[dev_dependencies]
915
gleeunit = ">= 1.0.0 and < 2.0.0"
16+
glinter = ">= 2.19.0 and < 3.0.0"
17+
18+
[tools.glinter]
19+
include = ["src/"]
20+
21+
[tools.glinter.rules]
22+
assert_ok_pattern = "off"
23+
discarded_result = "off"
24+
echo = "off"
25+
error_context_lost = "off"
26+
deep_nesting = "off"
27+
duplicate_import = "off"
28+
function_complexity = "off"
29+
label_possible = "off"
30+
missing_labels = "off"
31+
missing_type_annotation = "off"
32+
module_complexity = "off"
33+
panic_without_message = "off"
34+
prefer_guard_clause = "off"
35+
redundant_case = "off"
36+
short_variable_name = "off"
37+
string_inspect = "off"
38+
stringly_typed_error = "off"
39+
thrown_away_error = "off"
40+
todo_without_message = "off"
41+
trailing_underscore = "off"
42+
unnecessary_string_concatenation = "off"
43+
unnecessary_variable = "off"
44+
unqualified_import = "off"
45+
unused_exports = "off"
46+
unwrap_used = "off"

app/lib/wardwright.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule Wardwright do
88

99
@model_id "coding-balanced"
1010
@model_version "2026-05-13.mock"
11+
@model_definition_version 1
1112
@local_model "local/qwen-coder"
1213
@managed_model "managed/kimi-k2.6"
1314
@local_context_window 32_768
@@ -31,6 +32,7 @@ defmodule Wardwright do
3132
def model_id, do: @model_id
3233
def model_id(config) when is_map(config), do: Map.get(config, "model_id", @model_id)
3334
def model_version, do: @model_version
35+
def model_definition_version, do: @model_definition_version
3436
def local_model, do: @local_model
3537
def managed_model, do: @managed_model
3638
def local_context_window, do: @local_context_window
@@ -50,6 +52,7 @@ defmodule Wardwright do
5052
}
5153
],
5254
"governance" => [%{"action" => "transform", "id" => "prompt_transforms", "kind" => "request_transform"}],
55+
"model_definition_version" => @model_definition_version,
5356
"model_id" => @model_id,
5457
"policy_cache" => %{"max_entries" => 64, "recent_limit" => 20},
5558
"prompt_transforms" => %{},
@@ -281,6 +284,7 @@ defmodule Wardwright do
281284
"fallback_rate" => 0.0,
282285
"governance" => Map.get(config, "governance", []),
283286
"id" => model_id,
287+
"model_definition_version" => Map.get(config, "model_definition_version", @model_definition_version),
284288
"model_id" => model_id,
285289
"prompt_transforms" => Map.get(config, "prompt_transforms", %{}),
286290
"public_model_id" => model_id,
@@ -728,6 +732,10 @@ defmodule Wardwright do
728732
"cascades" => normalize_selectors(Map.get(config, "cascades", []), "models"),
729733
"dispatchers" => normalize_selectors(Map.get(config, "dispatchers", []), "models"),
730734
"governance" => Map.get(config, "governance", []),
735+
"model_definition_version" =>
736+
config
737+
|> Map.get("model_definition_version", @model_definition_version)
738+
|> positive_integer(@model_definition_version),
731739
"model_id" => model_id,
732740
"policy_cache" => normalize_policy_cache(Map.get(config, "policy_cache", %{})),
733741
"prompt_transforms" => Map.get(config, "prompt_transforms", %{}),

app/lib/wardwright/cli.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ defmodule Wardwright.CLI do
7878
"""
7979
end
8080

81-
defp admin_path(["access" | _]), do: "/admin/model-api-keys"
82-
defp admin_path(["keys" | _]), do: "/admin/model-api-keys"
83-
defp admin_path(_rest), do: "/policies"
81+
defp admin_path(["access" | _]), do: "/admin?view=model_access"
82+
defp admin_path(["keys" | _]), do: "/admin?view=model_access"
83+
defp admin_path(_rest), do: "/admin"
8484

8585
defp tools_help do
8686
tools = WardwrightWeb.PolicyAuthoringTools.cli_descriptions() |> Enum.join("\n")

0 commit comments

Comments
 (0)