Skip to content

refactor: add React Doctor CI and atomic machine connections - #308

Merged
ksarangmath merged 14 commits into
mainfrom
refactor/react-doctor-ci
Aug 14, 2026
Merged

ksarangmath merged 14 commits into
mainfrom
refactor/react-doctor-ci

Conversation

@ksarangmath

@ksarangmath ksarangmath commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add SHA-pinned React Doctor CI: pull requests block new errors in changed React code, while pushes to main run a non-blocking full audit. React Doctor is pinned to version 0.7.2.
  • Refactor the React findings from first principles: remove effect-driven derived state, consolidate resource combobox behavior, preserve stable pagination, query, selection, and streamed-message identity, and add focused serialization and domain tests.
  • Replace the multi-request BYO machine setup flow with an atomic POST /api/v1/orgs/{orgID}/machines/connect endpoint that creates the machine, daemon token, and only the selected project grants in one transaction.
  • Isolate forms rendered inside portaled dialogs so granting a model, machine, or pool from an agent builder cannot submit the surrounding Create Agent or Create Profile form.
  • Pre-bundle the Monaco YAML worker in Vite so first-time editor loading cannot trigger a delayed development-server reload after navigation.
  • Upgrade the Go toolchain and SHA-pinned Docker builder to 1.26.6 for the standard-library security fixes required by govulncheck.

Intentional behavior

  • Builder model selection is explicit; the first alphabetic grant is not treated as a product default.
  • Stale grants remain visible in the draft and are rejected authoritatively by the server instead of being erased by client effects.
  • Machine connection is all-or-nothing: a machine, token, and all requested grants are committed together or nothing is created.

Validation

  • make verify
  • make govulncheck: zero reachable vulnerabilities across the root and tool modules
  • Browser E2E: all 5 scenarios pass, including a valid outer Create Agent form that submits a nested grant dialog and asserts zero agent submission requests
  • Targeted storage and HTTP integration coverage for atomic creation, rollback, every selected-project authorization, and browser plus organization-manage requirements
  • Focused frontend coverage for SDK stream-error identity, checkbox accessibility semantics, and stable-key array pagination
  • Cold Vite dependency optimization: monaco-yaml and monaco-yaml/yaml.worker.js are pre-bundled and requesting the worker causes no optimizer reload
  • React Doctor 0.7.2 changed-scope audit: 100/100 for @omnara/react and @omnara/web
  • Independent Claude review: no blocking defects; all three actionable frontend findings were addressed and the stale description claims were removed

@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 8d15258.

@ksarangmath ksarangmath changed the title Add React Doctor CI and atomic machine connections refactor: add React Doctor CI and atomic machine connections Aug 5, 2026
@ksarangmath
ksarangmath force-pushed the refactor/react-doctor-ci branch from 2f548e6 to 6af329f Compare August 5, 2026 23:30
Comment thread internal/httpapi/machine_routes.go
@ksarangmath
ksarangmath force-pushed the refactor/react-doctor-ci branch 4 times, most recently from 737f5a4 to a1ef30d Compare August 7, 2026 03:52
@ksarangmath
ksarangmath requested a review from cmsparks August 7, 2026 04:37
@ksarangmath
ksarangmath force-pushed the refactor/react-doctor-ci branch from a1ef30d to f8f4aae Compare August 7, 2026 16:19
@ksarangmath
ksarangmath force-pushed the refactor/react-doctor-ci branch from f8f4aae to 760aeb3 Compare August 7, 2026 19:12

@cmsparks cmsparks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm!

# Conflicts:
#	api/openapi/openapi.yaml
#	docs/api-reference/openapi.yaml
#	frontend/apps/web/src/components/agents/AgentChatMessage.tsx
#	frontend/apps/web/src/components/agents/AgentConfigMcpServersField.tsx
#	frontend/apps/web/src/components/org/AddDiscoveredModelsStep.tsx
#	frontend/apps/web/src/components/org/CreateSecretDialog.tsx
#	frontend/packages/react/src/domains/agent-chat-messages.ts
#	frontend/packages/sdk/src/generated/index.ts
#	internal/e2e/web_e2e_test.go
#	internal/httpapi/openapi/api.gen.go
#	internal/storage/executionstore/machine_daemon_bootstrap_store.go
#	internal/storage/executionstore/machine_daemon_store.go
# Conflicts:
#	frontend/apps/web/e2e/web.spec.ts
#	frontend/apps/web/src/components/agents/AgentConfigBasicForm.tsx
#	frontend/apps/web/src/components/agents/AgentProfileTypeahead.tsx
#	frontend/apps/web/src/components/agents/AgentProfilesSection.tsx
#	frontend/apps/web/src/components/agents/AgentsSection.tsx
#	frontend/apps/web/src/components/ui/button.tsx
#	frontend/apps/web/src/routes/AgentProfileBuilder.tsx
#	frontend/apps/web/src/routes/CreateAgentPage.tsx
#	internal/httpapi/openapi/api.gen.go
# Conflicts:
#	frontend/apps/web/e2e/web.spec.ts
#	frontend/packages/sdk/src/generated/@tanstack/react-query.gen.ts
#	frontend/packages/sdk/src/generated/index.ts
#	frontend/packages/sdk/src/generated/sdk.gen.ts
#	internal/httpapi/openapi/api.gen.go
# Conflicts:
#	internal/httpapi/machine_routes_integration_test.go
#	internal/httpapi/openapi/api.gen.go
#	internal/storage/executionstore/machine_daemon_bootstrap_store.go
# Conflicts:
#	frontend/packages/sdk/src/generated/index.ts
#	internal/httpapi/openapi/api.gen.go
# Conflicts:
#	frontend/packages/sdk/src/generated/index.ts
#	internal/httpapi/openapi/api.gen.go
Comment thread api/openapi/openapi.yaml Outdated

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a8e93d6. Configure here.

plugins: [react({ babel: { plugins: [['babel-plugin-react-compiler', {}]] } }), tailwindcss()],
optimizeDeps: {
include: ['monaco-yaml/yaml.worker.js'],
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Incomplete Monaco Vite preload

Medium Severity

optimizeDeps.include only pre-bundles monaco-yaml/yaml.worker.js, while the lazy-loaded YAML editor still imports monaco-yaml on first open. That main-thread package can still trigger a delayed Vite dependency-optimization reload after navigation, which is the failure mode this change set out to prevent.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a8e93d6. Configure here.

@ksarangmath
ksarangmath merged commit 8ca0a79 into main Aug 14, 2026
22 checks passed
cmsparks added a commit that referenced this pull request Aug 14, 2026
The rebase onto main replayed this branch's editor rewrite over the
React Doctor restructuring (#308), so re-apply main's changes the replay
dropped and bring the editor suite in line with the new CI gate:

- Restore main's machines.ts (atomic connectByoMachine) and DataTable
  column API in AgentProfilesSection, re-applying this branch's edits.
- Adapt the machine source comboboxes to the new resource-combobox
  value contract via lightweight option objects.
- Fix React Doctor findings: refs instead of render state for
  handler-only values, drop compiler-redundant memoization, set-based
  lookups, and resolve the skills cache during render instead of an
  effect. Ignore the parent-callback rules for the editor files, whose
  children intentionally report yaml/dirty/availability upward.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cmsparks added a commit that referenced this pull request Aug 15, 2026
The rebase onto main replayed this branch's editor rewrite over the
React Doctor restructuring (#308), so re-apply main's changes the replay
dropped and bring the editor suite in line with the new CI gate:

- Restore main's machines.ts (atomic connectByoMachine) and DataTable
  column API in AgentProfilesSection, re-applying this branch's edits.
- Adapt the machine source comboboxes to the new resource-combobox
  value contract via lightweight option objects.
- Fix React Doctor findings: refs instead of render state for
  handler-only values, drop compiler-redundant memoization, set-based
  lookups, and resolve the skills cache during render instead of an
  effect. Ignore the parent-callback rules for the editor files, whose
  children intentionally report yaml/dirty/availability upward.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cmsparks added a commit that referenced this pull request Aug 17, 2026
The rebase onto main replayed this branch's editor rewrite over the
React Doctor restructuring (#308), so re-apply main's changes the replay
dropped and bring the editor suite in line with the new CI gate:

- Restore main's machines.ts (atomic connectByoMachine) and DataTable
  column API in AgentProfilesSection, re-applying this branch's edits.
- Adapt the machine source comboboxes to the new resource-combobox
  value contract via lightweight option objects.
- Fix React Doctor findings: refs instead of render state for
  handler-only values, drop compiler-redundant memoization, set-based
  lookups, and resolve the skills cache during render instead of an
  effect. Ignore the parent-callback rules for the editor files, whose
  children intentionally report yaml/dirty/availability upward.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cmsparks added a commit that referenced this pull request Aug 17, 2026
* ui improvements

* Improve agent edit UI for profiles and agents

* fix ci

* convert machines to machine binding id

* review fixes, adapt to document based yaml

* Refresh agent on config save conflict so discard loads the latest config

A 409 left the cached agent (and its current_config_id) stale, so
discarding remounted the editor with the same old source and expected id,
conflicting again on retry. Invalidate the agent query on 409 (awaited
before the mutation settles) and make discard re-snapshot from the live
query instead of falling back to the stale snapshot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Skip already-listed machines when granting from a source row

Granting from a row's combobox could duplicate machine_name entries and
reassign a selected row while keeping its cwd/env overlays. Drop names
already in the list, fill the current row only when it's empty, and
append the rest as fresh rows. Also apply prettier formatting CI flagged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Reconcile agent config editor with main's React Doctor refactor

The rebase onto main replayed this branch's editor rewrite over the
React Doctor restructuring (#308), so re-apply main's changes the replay
dropped and bring the editor suite in line with the new CI gate:

- Restore main's machines.ts (atomic connectByoMachine) and DataTable
  column API in AgentProfilesSection, re-applying this branch's edits.
- Adapt the machine source comboboxes to the new resource-combobox
  value contract via lightweight option objects.
- Fix React Doctor findings: refs instead of render state for
  handler-only values, drop compiler-redundant memoization, set-based
  lookups, and resolve the skills cache during render instead of an
  effect. Ignore the parent-callback rules for the editor files, whose
  children intentionally report yaml/dirty/availability upward.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Parse the config source once per editor via a serialization session

deserializeBasicConfig and applyBasicConfig each took the raw source, so
every builder change re-parsed the baseline and re-extracted it into a
second draft. createBasicConfigSession(source) parses and extracts once
for the editor's lifetime and returns { initialDraft, apply }; apply
clones the pristine document per call so reverting a field restores the
entry's original formatting. The form takes the session instead of
initialConfig + baselineSource.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Replace hand-rolled config extraction with a zod schema

extractBasicConfig walked the parsed document with manual typeof checks,
per-entry key allowlists, and generic list/map plumbing. A zod schema
now declares the builder-representable subset directly: strictObject is
the key allowlist, looseObject is the tolerated-extras top level, and
what remains is the typed mapping into draft rows. On the apply side,
tools and MCP entries compare by wire form, removing the parallel
comparable shapes; only machine sources keep a distinct comparison form,
since pool-resolution backfill changes their wire output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Consolidate the config editors and slim the mode machine

The agent panel editor and profile editor duplicated the mode machine
wiring, blocked/dirty/saveBlocked derivation, and the tabs/builder/YAML
layout. useAgentConfigEditor now owns that state and
AgentConfigEditorFields the shared markup; the editors keep only their
form, messages, and footer.

The mode reducer tracks just the hand-edited YAML (null while mirroring
the builder) instead of holding both buffers; builder YAML lives with
the component that produces it. wireConfig is gone — the no-document
fallback applies onto an empty Document through the same path as every
other serialization. The form's duplicate BasicConfigDraft type is
replaced by BasicConfig.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Strip comments and give the builder form a document-facing hook

Remove the explanatory comments this PR added across the frontend and
Go changes, keeping only functional directives. useAgentBuilderForm now
owns the builder draft: it seeds from the session's document, exposes
form values and named setters, and reports serialized YAML plus the
blocked flag; AgentConfigBasicForm is rendering only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Fold agentConfigBasic into useAgentBuilderForm

The draft types, empty draft, and completeness check belong to the form.
isBasicConfigComplete becomes per-section validators (draftValid,
machineSourceValid, mcpServerValid, mcpServerNamesUnique) private to the
hook; other modules import the draft types from useAgentBuilderForm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Move the document session and apply into useAgentBuilderForm

The hook is the only thing that applies drafts to the document, so
agentConfigBasicYaml.ts folds into it: session creation, the
diff-and-rewrite apply, and the wire serializers now live with the form
that uses them. The two test files and their fixtures merge into
useAgentBuilderForm.test.ts. agentConfigBasicExtract.ts stays separate
as the schema-validated read side (folding it in would break the
500-line lint limit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Adopt builder-representable YAML edits when switching back to the builder

Switching from the YAML tab to the builder previously always demanded
discarding hand edits. Now the typed YAML runs through
createBasicConfigSession: when it is builder-representable it becomes
the new session baseline and the form reseeds from it, so the edits
carry over; the confirm-and-discard dialog remains only for YAML the
builder cannot represent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Derive builder YAML from the form instead of mirroring it upward

The editor held builderYaml state fed by an onYamlChange callback
because the draft lived inside the form component. useAgentBuilderForm
no longer takes a callback: the editor hook owns the form, and yaml and
blocked are derived from the draft (session.apply(draft)). That deletes
the mirror state, the callback effect, and the formGeneration remount on
YAML adoption, which now just resets the draft. AgentConfigBasicForm
takes the form object and stays render-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Fix YAML adoption: collapse the fork with an explicit action

Adopting builder-representable YAML edits never worked: switchMode
collapsed the fork by dispatching editor-yaml-changed with the forked
text itself, which the reducer's no-op guard swallowed, so switch-mode
still saw a fork and raised the discard dialog for every edit. A
dedicated adopt-yaml-edits action enters the builder and clears the
fork; reducer tests pin the transitions. The dialog copy now says it
only appears for YAML the builder can't represent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Prune library-behavior and duplicate tests

Drop the form tests that only exercised the yaml/zod libraries
(formatting-variant parsing, explicit empty parameters, irregular
instruction indentation) and merge the discard reducer test into the
confirm-dialog transition it belongs to; adopt keeps its own regression
test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* gofmt agent_config_changes_store.go

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Regenerate sqlc code after SQL comment removal

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Integrate main's agent templates with the builder-form create page

Rebase reconciliation: main added an onboarding template flow built on
agentConfigBasicSerialization, which this branch had replaced with the
useAgentBuilderForm session API. Rewrite CreateAgentFormView on the new
API (template menu, ?template= prefill, message placeholder preserved),
re-point agentTemplates at the builder module, and delete the retired
serialization module. Carry main's GB memory rename through the builder
form, extract, and machine sources field. Regenerate the TS SDK from the
merged spec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Serialize an untouched builder form to an empty YAML document

With no baseline config, applyToDocument wrote empty-string scalars into
a fresh document, so the YAML tab on the create page opened with an
instruction/model skeleton instead of an empty editor and typed YAML
merged into it invalidly. Compare scalars against an empty baseline so
only filled fields are written.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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