Skip to content

feat(builder): conversational AI intent builder shell - #6610

Merged
delchev merged 2 commits into
masterfrom
feat/intent-builder-shell
Aug 7, 2026
Merged

feat(builder): conversational AI intent builder shell#6610
delchev merged 2 commits into
masterfrom
feat/intent-builder-shell

Conversation

@delchev

@delchev delchev commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

A new standalone Harmonia shell — a conversational AI intent builder. You describe the application you want; an app.intent is created and evolved underneath; a canvas visualises the entities, processes and glue live; one button turns the conversation into a running application, ending on a success state that links you to the shells where you can try it.

Served at /services/web/builder/ (shell id builder), .access-gated to ADMINISTRATOR / DEVELOPER like the intent endpoints it drives.

Almost no new backend

The feature is a frontend orchestration over endpoints that already ship — the intent engine's agent / parse / generate, the workspace, the publisher, and the health + problems feeds — reusing the shared application-core runtime (theme, notifications, branding, i18n) by absolute URL, with no per-shell copies. It is one screen, so there is deliberately no Pinecone router.

The single addition is GET /services/ide/intent/agent/status{configured} (configuration-only, never an upstream call). The shell probes it on load so an instance with no API key says so before the user types, instead of letting the first message fail with 412. The plan proposed firing a throwaway /agent turn for this, which would have burned a real Anthropic round-trip on every page load of a working instance; the endpoint is the cheap way to get the banner up front.

The conversation loop

chat → POST /services/ide/intent/agentre-validate the proposal client-side through /parse → auto-apply → redraw.

The re-validation is load-bearing, not belt-and-braces: the agent returns 200 even when its proposal still fails validation (the server's two repair rounds were exhausted), signalling it only as trailing prose in reply. An invalid proposal is rendered as issues and never reaches the buffer. There is no diff/Accept step — a valid proposal auto-applies and the assistant's own text is the change narration; the raw YAML stays available read-only in a drawer, and the Intent Editor in the IDE remains the authoring surface.

Hidden persistence

The project id is a slug of the intent's name, created lazily on the first accepted proposal and never renamed (a rename would mean delete + recreate + republish). Every accepted proposal is auto-saved, so reloading restores the app from disk, not from browser memory. An app switcher lists the workspace projects carrying a root app.intent; chat history is per-project localStorage (the agent endpoint is stateless).

The one button

save → Problems baseline → generate models → replay codeGenerations → publish → healthcheck poll + Problems diff, each step ticking green.

The Problems diff is the only honest "published clean" signal: publishing is synchronous but reports nothing about what the synchronizers then made of the artefacts, so a client-Java compile error or a failed CSVIM seed surfaces there and nowhere else. Without it a broken app would be reported as a success. The success panel's "try it here" links are discovered (platform-shells + the app's own perspective path from application-perspectives) rather than assembled from a guessed gen/<model> convention that would 404 the moment a template changed its layout.

Shared diagram renderer (the only change to shipped code)

The Intent Editor's ~310 lines of mxGraph rendering are extracted from its AngularJS controller into a framework-free window.IntentDiagrams.render(model, host) / .dispose(host) (editor-intent/js/intent-diagrams.js). Mechanical extraction only — the editor renders through it unchanged (editor.js drops from 824 to 491 lines) and the shell loads the same file by URL. IntentEditorLoadsIT guards it.

Gotchas this paid for

Each of these was found by the integration test, not by review:

  • Never put an Alpine binding on <i x-h-lucide>. The plugin replaces the <i> with the rendered svg, so a binding would be lost — it throws instead, and the uncaught throw aborts Alpine's walk over everything below it (the toolbar rendered; the entire split pane did not). Use an <svg x-h-lucide> placeholder.
  • The idempotent creates answer 304, and response.ok is 2xx-only — so a naive !response.ok throw made the second save fail while the first succeeded.
  • The workspace itself may not exist on an instance where the user never opened the IDE; creating a project inside a missing workspace answers 404.
  • The agent endpoint can make three upstream calls at 120 s each, so the client timeout is 7 minutes with a staged typing indicator.

Tests

IntentBuilderShellIT drives the journey against a local HTTP stub of the Anthropic upstream (DIRIGIBLE_INTENT_AI_BASE_URL, the same override IntentAgentServiceTest uses in-process), so it needs no API key and no network:

  • @Tag("smoke") — the shell bootstraps (stores + renderer registered, first-run surface renders). Runs on every PR; this is the regression class services-level tests cannot catch, where every endpoint stays green while the page is dead.
  • Unconfigured assistant — with no key the banner is announced before the user types; with the stub configured it stays away.
  • Full journey — one sentence becomes a validated intent, the canvas draws it, the intent is saved with nothing asked of the user, and Publish reaches the success state. Backed by assertions on what the pipeline actually produced (the generated .model in the workspace, the project in the registry), so it stays honest if the success panel is ever reworked.

Verified locally: IntentBuilderShellIT (both) green, IntentEditorLoadsIT green, formatter:validate BUILD SUCCESS, full reactor build SUCCESS, unit suite 1483 tests / 0 failures (the 5 errors are FtpRepositoryTest + DirigibleApplicationTest BindException: Address already in use from another local instance holding the ports).

Deliberately not in v1

No raw YAML editing (read-only drawer), no diff/Accept, no server-side conversation persistence, and no multi-model (uses:) orchestration.

🤖 Generated with Claude Code

delchev and others added 2 commits August 7, 2026 20:04
Add `resources-builder`, a standalone Harmonia shell at
/services/web/builder/ where you describe an application in plain
language and one button turns the conversation into a running app.

It adds no backend: the whole feature is a frontend orchestration over
endpoints that already ship (the intent engine's agent/parse/generate,
the workspace, the publisher, the health and problems feeds), reusing
the shared application-core runtime by absolute URL.

The loop is chat -> agent -> re-validate the proposal client-side
through /parse -> auto-apply -> redraw the canvas. The re-validation is
load-bearing rather than defensive: the agent answers 200 even when its
proposal still fails validation (the server's two repair rounds were
exhausted), signalling it only as trailing prose in the reply, so an
invalid proposal is surfaced as issues and never reaches the buffer.

Project and file management stay invisible. The project id is a slug of
the intent's name, created lazily on the first accepted proposal and
never renamed; every accepted proposal is auto-saved, so a reload
restores the app from disk rather than from browser memory.

Publish runs a staged pipeline: save, snapshot the Problems feed,
generate the models, replay the code generations, publish, then poll
healthcheck and diff Problems for this project. That diff is the only
honest "published clean" signal - publishing is synchronous but says
nothing about what the synchronizers made of the artefacts, so a
client-Java compile error or a failed CSVIM seed surfaces there and
nowhere else. The "try it here" links are discovered from the
platform-shells and application-perspectives services, never assembled
from a guessed gen-folder convention.

The mxGraph rendering is extracted from the Intent Editor's controller
into the framework-free IntentDiagrams module and shared, not copied -
the editor keeps rendering through it (IntentEditorLoadsIT covers that)
and the shell loads the same file by URL.

IntentBuilderShellIT drives the journey against a local HTTP stub of the
Anthropic upstream via DIRIGIBLE_INTENT_AI_BASE_URL, so it needs no API
key and no network; a @tag("smoke") bootstrap check runs on every PR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nt is unavailable before the first message

Add `GET /services/ide/intent/agent/status` -> `{configured}`, backed by
`IntentAgentService.isConfigured()`. It reads configuration only and
never calls the model, which is the whole point: the Builder shell
probes it on load, so an instance with no API key says so before the
user types instead of letting the first message fail with 412.

The alternative the plan proposed - firing a throwaway /agent turn on
load - would have burned a real Anthropic round-trip on every page load
of a working instance. This endpoint is the cheap way to get the banner
up front.

The key is read live rather than cached, so a key set after start-up is
picked up; an unreachable status endpoint is treated as configured, so a
probe failure never puts a false "not configured" banner over a working
shell.

Covered by IntentEngineIT (200 + configured=false in the key-less test
environment, network-free, so it runs on every PR) and by two
IntentBuilderShellIT assertions: the banner appears with no key, and
does not appear when the assistant is configured.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@delchev
delchev merged commit 0d7b240 into master Aug 7, 2026
10 checks passed
@delchev
delchev deleted the feat/intent-builder-shell branch August 7, 2026 18:49
delchev added a commit that referenced this pull request Aug 7, 2026
Master gained the Builder shell (#6610), which registers itself on the
Home launchpad exactly where this branch registers Monitoring - so both
sides added an entry to the same two constants in home.js.

Resolved additively, keeping both shells. SECONDARY stays ordered
least-technical-first per its own comment: the two operations tools
(Admin, Monitoring) then the two building tools (Builder, Workbench).

The Maven wiring in components/pom.xml and group-ui merged cleanly and
was verified to carry both modules.
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