Skip to content

feat: release AgencyZero on UI 3.2 and WorkTable 1.10 beta - #211

Open
pathscale wants to merge 27 commits into
masterfrom
feat/ui-3-migration
Open

pathscale wants to merge 27 commits into
masterfrom
feat/ui-3-migration

Conversation

@pathscale

@pathscale pathscale commented Sep 7, 2026

Copy link
Copy Markdown
Owner

AgencyZero now takes the released UI 3.2 line and is ready for WorkTable 1.10 beta's v3 page format without dropping existing stable or experimental profile data.

The GUI resolves @pathscale/ui 3.2.3 with the Solid 2.0 RC packages on one compatible line. Native behavior fixes cover retained PillMenu paint, immediate project-item reveal, Settings search reset, transparent variant windows, and the ps-qa control bindings. The app version is 0.8.61.

Storage and runtime changes:

  • every current WorkTable consumer resolves the workspace caret ^1.10.0-beta1;
  • shared stable and experimental bootstrap detects v2 before opening any application table;
  • an internal wt-migrate/v2-reader sidecar reads the immutable compatible v2 registry pair, exports neutral archives for all 17 tables, and stays outside the beta runtime graph;
  • migration builds a separate v3 stage, checks every table count and full-row SHA-256 digest, strict-loads the v3 tables, closes each table so the stage is durably saved, then promotes it;
  • after close and cold validation, every staged data/index file and nested directory is synced bottom-up before the durable validated marker permits crash recovery to promote it;
  • failure leaves the v2 source at the live path, including interrupted export/import and failed promotion recovery;
  • successful durable promotion deletes the displaced v2 directory. A later launch sees the completed v3 state and skips conversion;
  • headless storage work uses Nagoya's default locality. The GUI retains direct Tokio only at Tauri and AgencyProxy boundaries whose APIs expose concrete Tokio tasks, sockets, processes, signals, timers, channels, and async I/O types;
  • the lifecycle ownership work from refactor: take the control surface from the protocol, not the runtime #212 and its descriptor-by-PID QA fix are folded here;
  • repository policy now prohibits adding Node.js, npm, npx, pnpm, Yarn, or Deno tooling. The existing frontend remains on Bun.

Copied-profile end-to-end evidence, with no live profile changed:

  • stable: 17 tables, 63,868 rows, with every count and full-row digest matching after strict cold reopen;
  • experimental: 17 tables, 59,212 rows, with every count and full-row digest matching after strict cold reopen;
  • a stable copy at a dotted custom path promoted to header 03000000, removed its temporary v2 directory, recorded phase complete, and skipped cleanly on the second launch;
  • a forced reader failure left the experimental v2 copy byte-identical and created no displaced backup;
  • interrupted validated and source-preserved phases recover through the same promotion state machine.

Focused validation at current storage-migration head 8a20e95, patching WorkTable from the exact PR #106 head c631e0b:

  • cargo test -p wt-migrate: 18 passed;
  • cargo clippy -p wt-migrate --all-targets -- -D warnings: passed;
  • cargo fmt --all -- --check and git diff --check: passed;
  • no Cargo or Bun lockfile is committed.

Earlier cutover validation at f4535d6, using the same PR #106 implementation line:

  • cargo check -p az-gui --no-default-features --features webview-runtime: passed;
  • cargo clippy -p wt-migrate -p az-gui --no-default-features --features az-gui/webview-runtime --all-targets -- -D warnings: passed;
  • cargo test -p agency-tools -p wt-migrate: passed, including 29 agency-tools tests and 18 wt-migrate tests;
  • internal v2 reader cargo check and cargo clippy --all-targets -- -D warnings: passed.

The previously recorded UI candidate evidence remains 41 frontend files and 394/394 tests, with production build, TypeScript, lint, and UI ownership checks passing. Desktop and GPU QA were not rerun for this storage/runtime pass.

Registry-only CI is expected to remain red until worktable 1.10.0-beta1 is published. The consumer gate above used the review-ready WorkTable PR head rather than an application workaround.

The 0.8.61 update adopts WorkTable's final typed mutation surface: declared writes pass the row key, a generated TableColumns selector, and the typed value; complete rows use replace. All 22 active GUI mutation callsites were migrated without changing schema grammar. cargo check -p az-gui --all-targets passes against WorkTable checkpoint 72b018f, using the already-built frontend and local sidecar placeholders only for Tauri's build-time file checks. No JavaScript runtime ran. Registry order is WorkTable #106 and publication, then this PR.

meh added 7 commits September 6, 2026 18:07
Every Settings switch lies about its setting on the 0.3 engine. `checked` is an
HTML boolean attribute, so the `checked="false"` that a controlled component
writes reads as checked, and the property never reached the input's live state
at all. A setting stored `false` comes back reporting on after a restart, and
only agrees with itself once someone clicks it.

Measured on a build against the fixed engine, same disposable profile either
way:

    stored blitzDeepProfilingEnabled: false
    0.3:  switch ... "visible,selected"
    0.4:  switch ... visible

`tests/ps-qa/06-toggles.ron` could not see it. `ValueChanges` is satisfied by the
renderer flipping the input natively, whether or not the application hears about
it, and every toggle check presses first and asserts a delta, which is exactly
what a wrong initial value survives. A check comparing a control against its
persisted setting without pressing it is what would have caught this.

The full native suite is 310/310 on the fixed engine.

Pinned at 0.4.3 rather than 0.4: 0.4.2 is a newer engine without the fixes.
The release workflow declines to publish when the committed version equals the
live one, so the renderer fix would merge and reach no user. Every Settings
switch on 0.8.50 reports the wrong state for a setting that is off, until
someone clicks it.
`^0.4.3` and `^0.3.3` are version bumps wearing carets. `^0.4` and `^0.3` are
the convention here; the lockfile decides which patch is actually used.
Two of the ten breaking changes reach this frontend.

Value-change callbacks now report the value, so `Switch` and `Checkbox`
take `onChange={(checked) => ...}` rather than an `Event`. The native
event is `onNativeChange`. The cleanup row keeps that one: its box is
rendered permanently checked, unchecking it is the keep action, and a
keep that fails has to put the tick back by writing to the element,
which only the native handler hands over.

`Input` moved its default size from `md` to `sm` so an unsized field
lines up with an unsized button. None of the eleven fields here set a
height of their own, so each names `md` to keep the row it was built
for. Which of them would rather be shorter is a look decision, not a
migration one.

The other eight do not apply: no `className`, none of the renamed
components, no `isDisabled`/`isOpen` on a library component (those
spellings are this app's own wrappers), no `color`, no query
accessors and no `PasswordField`. The Layouts application compiler was
already configured.

Typecheck, both contract checks and 394 tests pass against 4.0.0.
4.0.0 was published in error and has been unpublished from the registry, so a
`^4.0.0` range now resolves to nothing at all. The value-change contract these
conversions target ships as 3.1.0.

The source is unchanged: it was written against this API either way. Only the
number the range asks for is different.
`solid-js` and `@solidjs/web` were both `"next"`, and `bun.lock` is gitignored
here, so nothing recorded what that resolved to. Today it is 2.0.0-rc.4 for
every other repository in the fleet and 2.0.0-rc.6 here, decided by whichever
day someone last installed. On a library still in release candidates that is a
build whose behaviour changes without a commit.

Pinned to 2.0.0-rc.4, which is what the rest of the fleet and the published
`@pathscale/ui` peer set are built against.

Typecheck, both contract checks and 394 tests pass on 3.1.0 with this pin.
@pathscale pathscale changed the title feat(gui): migrate to @pathscale/ui 3.1.0, and pin Solid feat(gui): release AgencyZero on UI 3.2 Sep 12, 2026
@pathscale pathscale changed the title feat(gui): release AgencyZero on UI 3.2 feat: release AgencyZero on UI 3.2 and WorkTable 1.9 beta Sep 13, 2026
@pathscale pathscale changed the title feat: release AgencyZero on UI 3.2 and WorkTable 1.9 beta feat: release AgencyZero on UI 3.2 and WorkTable 1.10 beta Sep 13, 2026
meh added 3 commits September 15, 2026 02:23
Adds Agent::Grok across detection, models, pricing, quota, settings and
the run loop, plus the frontend surfaces that name a provider.

Grok specifics the other providers do not need:

* Sessions are keyed by process cwd, like Claude's. `grok_session_cwd`
  reads `~/.grok/sessions/<urlencoded-cwd>/<id>` so resume and compact
  run where the session actually lives, instead of answering JSON-RPC
  -32603 and surfacing it as a parse error.
* Prices double at 200k for the whole prompt, so a turn is steered at
  180k and again at 190k, then compacted after the turn ends rather than
  mid-tool.
* ACP does not execute `<tool_call>` markup written into assistant text.
  When that leaks the turn ends having done nothing, so the run resumes
  with a correction naming native tools.
* Auto still emits `session/request_permission` for writes outside the
  workspace, so the approval channel opens and `auto_allows` answers.

Also records a foreign namespace Prompt Syntax span rather than letting
it render raw. A `<ps @Antml:invoke>` from a model blending grammars is
a tool call it believed it made: the span stays inert, per PS 13.2, but
every one is now logged with its verb and arguments, written to the
transcript, and handed back so the call can be reissued natively.
Two latent gaps found reviewing the Grok work.

`user_local_bin_is_searchable_without_overriding_shell_order` passed by
accident: its input PATH already contained `/opt/homebrew/bin`, so the
appended copy was deduplicated away and the indices lined up whatever
order the candidates were pushed in. Start from a PATH holding none of
them, so the order is actually read.

`price_for` is first match wins on substring, so `grok-4.6` must precede
`grok-4.5` or the shorter key shadows the longer one and bills the wrong
cache read rate (0.50 against 0.30). Nothing enforced that. Check the
whole table, not just the pair that prompted it.
Every table in the committed archive was v2. The app converts a v2 store
on load, so each QA run migrated the fixture again before it could open
it, and a restore never handed the harness the format the build actually
uses.

Converted through `wt-migrate migrate-v2-store`: 17 tables, 27314 rows,
all verified. A restored copy now boots straight into the store with no
conversion step in the log.
@pathscale

Copy link
Copy Markdown
Owner Author

Added: Grok as a project agent, plus two dependency PRs

Four commits on top of the UI 3.2 / WorkTable 1.10 work.

Depends on (merge these first)

PR Why this branch needs it
pathscale/RustAgentAbstraction#36 --no-plan (ACP has no TUI, so plan mode hangs the turn) and RateLimit::used_percent. AZ will not build without it.
pathscale/ps-blitz#99 Hoisted paint child duplicated on incremental resolve. Fixes the last ps-qa failure on this branch.

feat(gui): drive Grok as a project agent

Agent::Grok across detection, models, pricing, quota, settings and the run loop, plus the frontend surfaces that name a provider. Grok specifics: cwd-keyed session resume, a 180k/190k steer before the 200k price cliff, recovery when <tool_call> markup leaks into assistant text, and an approval channel under Auto.

Also records foreign-namespace Prompt Syntax spans instead of letting them render raw. A <ps @antml:invoke> is a tool call the model believed it made; the span stays inert per PS 13.2, but each one is now logged with verb and arguments, written to the transcript, and handed back so the call can be reissued natively.

test(gui): cover the macOS PATH candidates and price key shadowing

Two latent gaps found while reviewing the Grok work. The PATH test passed by accident (its input already contained /opt/homebrew/bin, so dedup hid the new candidates). And price_for is first-match-wins on substring, so grok-4.6 must precede grok-4.5 or the wrong cache-read rate is billed, with nothing enforcing it.

test(qa): store the QA profile fixture in WorkTable page format v3

Every table in the committed archive was v2, so each QA run re-migrated the fixture before it could open it. Converted through wt-migrate migrate-v2-store: 17 tables, 27314 rows verified.

Testing

  • cargo test -p az-gui --bins: 325 passed, 0 failed. Clippy clean.
  • ps-qa full suite against the v3 fixture: 312/312, exit 0 (was 311/312 before the ps-blitz fix).
  • Frontend: 284 pass, 9 fail. All 9 are a pre-existing jsxDEV module-resolution error, identical on master; this branch adds 6 passing tests.

The catalogue is four agents now, ordered claude, codex, grok, copilot,
and the prompt list carries grok-4.6 and grok-4.5. Also corrects a label
assertion that predates this work: the pill reads "Codex" because that
is what `agentLabel` has, not "OpenAI".

CI never reached these before. The Rust build failed first on a
worktable requirement, so the frontend suite did not run.
@pathscale

Copy link
Copy Markdown
Owner Author

Merge order and remaining blocker

Consolidated to one PR per repo. RustAgentAbstraction#36 was closed and cherry-picked onto #35 (same Grok branch), so history stays linear with no merge commits.

Order

# PR CI Why it goes here
1 pathscale/WorkTable#106 (1.10.0-beta1) Formatting fails Nothing else builds on CI until this is published. Not touched by this work.
2 pathscale/RustAgentAbstraction#35 (0.4.21) green Publishes the Grok ACP transport this branch calls.
3 pathscale/ps-blitz#99 (0.4.10) green (7 pass, 3 skip) Independent. Any time after its own review.
4 this PR Frontend green, Rust blocked Needs 1 and 2 on crates.io.

Version bumps

All present: WorkTable 1.10.0-beta1 (#106), agent-abstraction 0.4.21, ps-blitz 0.4.10, az 0.8.64 (bumped in the Grok commit). ps-blitz 0.4.10 does not collide with #98, which detached blitz-net to its own version.

The Rust failure is two path overrides, not this work

worktable = { path = "../WorkTable" }
agent-abstraction = { path = "../../../RustAgentAbstraction" }

Both point outside the repo, so CI cannot load the manifest. They are local debugging overrides that predate this work (master has worktable = "^1.0.0-beta" and agent-abstraction = "0.4.19"). Once 1 and 2 publish, these go back to ^1.10.0-beta1 and 0.4.21. I have left them as paths for now because flipping them before the crates exist would only trade one red build for another. Say the word and I will push that commit.

Fixed since the last comment

  • fix(grok): satisfy clippy under -D warnings on Compact AGENTS.md, fix copy inside the prompt box #35. CI runs -D warnings, and the new ACP module had 9 errors (separators, map().unwrap_or() on a Result, bool count, JSON number casts). Compact AGENTS.md, fix copy inside the prompt box #35 is green now.
  • test(frontend): expect Grok in the catalogue and the prompt list. CI never reached the frontend suite before, because the Rust build failed first. With the build further along, 3 stale assertions in models.test.tsx surfaced: the catalogue is four agents (claude, codex, grok, copilot), the prompt list carries grok-4.6/grok-4.5, and one label assertion predates this work (agentLabel says "Codex", not "OpenAI").

Frontend is 402/402 under vitest. Locally bun test reports 9 failures from a jsxDEV resolution quirk that vitest, which CI uses, does not hit.

`spawn_resume_after_compact` ran on every successful compaction, so an
owner pressing Compact was answered with a turn they did not ask for.

Compaction interrupts in-flight work only when the app starts it. Grok
is compacted before the 200k price cliff, mid-task and unasked, and
leaving that summarised and idle strands the work that triggered it. An
owner choosing the moment is the opposite: the next instruction is
theirs to give.

So the resume follows the trigger, not the agent, and an owner-driven
compaction of a Grok project stays silent too. The `compact_project`
command keeps its signature and hardcodes `Owner`, which leaves no
resume parameter on the manual path and nothing the frontend can set.
Only `compact_project_with`, which is not registered for IPC, can ask
for `Automatic`.
@pathscale

Copy link
Copy Markdown
Owner Author

Status: one blocker, upstream, not fixable from this branch

Frontend is green. Rust and Panel checks both fail, and both fail for the same single reason, in stage-agency-proxy-sidecar.sh before any compilation:

failed to load manifest for workspace member `.../apps/gui`
  failed to load manifest for dependency `agent-abstraction`
  failed to read `.../RustAgentAbstraction/Cargo.toml`: No such file or directory

Two tracked path overrides point outside the repository:

worktable         = { path = "../WorkTable" }              # Cargo.toml:30
agent-abstraction = { path = "../../../RustAgentAbstraction" }  # apps/gui/Cargo.toml:67

CI deliberately does not commit Cargo.lock, so the sidecar script runs cargo generate-lockfile first, and resolution fails there. Master carries worktable = "^1.0.0-beta" and agent-abstraction = "0.4.19", so these overrides are local debugging state that predates this work.

Why I have not simply reverted them

I tried it. Today it fails differently, not better:

error: failed to select a version for the requirement `agent-abstraction = "^0.4.21"`
candidate versions found which didn't match: 0.4.19, 0.4.18, 0.4.17, ...

Published maximums right now are agent-abstraction 0.4.19 and worktable 1.9.0-alpha1. This branch needs 0.4.21 and 1.10.0-beta1. Flipping the paths before those publish trades "failed to load manifest" for "failed to select a version", which is the error this branch showed before the overrides were added.

The unblock

  1. Merge and publish Release WorkTable 1.10.0 beta1 WorkTable#106worktable 1.10.0-beta1
  2. Merge and publish feat: Grok over grok agent stdio RustAgentAbstraction#35agent-abstraction 0.4.21 (green)
  3. One commit here flipping both overrides back to version requirements
  4. Rust and Panel checks go green together

pathscale/ps-blitz#99 is independent (7 pass, 3 skip) and can merge any time; AgencyZero consumes ps-blitz through .cargo/config.toml, which is gitignored and never reaches CI.

Everything else on this branch is verified locally: 326 Rust tests, clippy clean, 402/402 frontend under vitest, and the ps-qa suite 312/312 against the v3 fixture.

meh added 4 commits September 15, 2026 03:59
WorkTable and agent-abstraction are taken by path, from sibling
checkouts, so a change can be made and tested across all three without
publishing between each step. CI had no such siblings, so `cargo
generate-lockfile` failed inside the sidecar script before anything was
compiled, and both the Rust and QA panel jobs died there.

Check them out and move them beside the workspace. `actions/checkout`
refuses a path outside the workspace, so they land in `.sibling` first.

`ref` follows the branch carrying the matching change and moves to the
default branch once that merges. Version requirements are deliberately
not used here: they would force a publish before a change could be
tested, which is the coupling the path dependencies exist to avoid.
`cargo fmt --all` reaches into path dependencies, so with the sibling
checkouts present it began reporting WorkTable's formatting as this
repository's failure. Those repositories gate their own formatting in
their own CI.

Naming each workspace member keeps the job answering for this repository
alone. Doing that surfaced a real diff in `projects.rs`, now formatted.
…orner

Message text and the prompt field both carried `overflow-wrap: anywhere`
alongside `break-words`. The two are not the same: `break-word` splits a
word only when that word alone cannot fit, while `anywhere` also counts
the break opportunity when the renderer measures min-content width, so
it split ordinary prose mid-line and ended a line on "No" with "w" under
it. `break-words` alone still rescues a long identifier or url, which is
what the aggressive value was reached for.

The composer's inner surface also had no `overflow-hidden`. Its radius
is the ring's outer radius less the ring's 1px padding, so the first
attachment chip drew over the corner the ring had rounded and squared
off the top-left while the right stayed round.
`resize` sets `height: auto` when the text may have got shorter, so
`scrollHeight` reports the content rather than the old box. The write
back was then guarded on the height having changed, and a deletion that
removed a character without removing a line measured the same number,
skipped the write, and left the field on `auto`.

That is why deleting text made the prompt jump. `auto` drops the
explicit height, so the field sized itself to its content while the
viewport wrapper kept the pixel height of the previous measurement and
clipped it. Over the ceiling it was worse: the field rendered at full
content height inside a 168px clipped wrapper instead of scrolling.

Write the height whenever the reset ran, not only when the number moved.
@pathscale

Copy link
Copy Markdown
Owner Author

Added: composer and transcript text rendering

Two rendering defects reported against the running app, both confirmed fixed by the owner in a QA-profile build.

fix(frontend): stop splitting ordinary words, and clip the composer corner (b90775d)

Words split mid-line. MessageBody and the Composer textarea each carried overflow-wrap: anywhere alongside break-words. Those differ in one way that matters: break-word splits a word only when that word alone cannot fit, while anywhere also counts the break opportunity when the renderer measures min-content width, so it splits ordinary prose. A line ended on "No" with "w" beneath it. break-words on its own still rescues the long identifier or url the aggressive value was reached for.

Composer corner squared off. The inner surface had no overflow-hidden. Its radius is the ring's outer radius less the ring's 1px padding, so the first attachment chip drew over the corner the ring had rounded, squaring the top-left while the right stayed round.

fix(frontend): restore an explicit prompt height after the shrink reset (56a2536)

resize sets height: auto when the text may have got shorter, so scrollHeight reports content rather than the old box. The write-back was guarded on the height having changed:

if (mayHaveShrunk) field.style.height = "auto";
const height = ...;
if (height !== lastHeight) { field.style.height = `${height}px`; }   // skipped

A deletion that removed a character without removing a line measured the same number, skipped the write, and left the field on auto. With no explicit height it sized itself to its content while the viewport wrapper kept the previous pixel height and clipped it, then snapped on whichever later keystroke moved the number.

Above the ceiling it was worse: Math.min(scrollHeight, 168) equals lastHeight, so a long prompt rendered at full content height inside a 168px clipped wrapper instead of scrolling.

The auto reset is itself a style write, so an explicit height has to be restored whenever it ran, not only when the number moved.

Verification

  • Frontend 402/402, typecheck clean, lint exits 0
  • ps-qa against the running build: composer 22/22, textarea 3/3, transcript 2/2
  • Owner confirmed both behaviours fixed in a QA-profile build

Still open, and not app code

Inline code spacing renders without the surrounding spaces (note + `ident` + reads run together). I traced the split and the JS preserves whitespace correctly, so the collapse is in Blitz's inline text layout rather than here. Not addressed in this PR.

The step reported `passed: 312, failed: 0` and every contrast audit
clean, then its own 3 minute timeout killed it at 3m00s. The previous
green run finished in 2m33s, so the margin was 27 seconds and the suite
grew past it.

This bounds the wall clock of the whole sweep only. `QA_TIMEOUT_SCALE`
still governs whether any individual outcome may be slow, so no
assertion is weakened.
@pathscale

Copy link
Copy Markdown
Owner Author

CI green

ci: give the complete behavior audit room for the suite it now runs (1704b50)

The QA panel job failed once on the UI commits, and the sweep was not the reason. That run reported passed: 312, failed: 0 with every contrast audit clean, then the step's own 3 minute timeout killed it at 3m00s. The previous green run finished in 2m33s, so the margin was 27 seconds and the suite grew past it rather than regressing into it.

Raised to 6 minutes. This bounds the wall clock of the whole sweep only; QA_TIMEOUT_SCALE still governs whether any individual outcome may be slow, so no assertion is weakened. The job cap stays 30 minutes.

Checked locally before changing it: the full sweep runs in 132s against this build, and the composer group is 22 checks in 3.9s total, none near the 1250ms per-check limit. The two commits here remove a CSS class and add one style write, so there was no plausible cost to find.

Frontend, Rust and Panel checks are all green on 1704b50.

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