Skip to content

Simulation - #21

Closed
chenkasirer wants to merge 10 commits into
mainfrom
simulation
Closed

Simulation#21
chenkasirer wants to merge 10 commits into
mainfrom
simulation

Conversation

@chenkasirer

Copy link
Copy Markdown
Contributor

No description provided.

chenkasirer and others added 10 commits July 21, 2026 15:01
Add a Save button to the author toolbar that POSTs the current
blueprint to /blueprints/upload unmodified, reusing Export's
validation and serialisation. Guards against silently overwriting
an existing blueprint id by checking GET /blueprints first and
prompting for confirmation.
…tarts a session

Adds a Simulate action to the author toolbar per ADR-0003: rewrites every
non-system task type with a `simulation.` prefix, refuses blueprints
containing system.composite tasks, uploads the derived blueprint under
`{id}__sim`, starts a session from it, records the driving tab in
sessionStorage, and hands the tab to the dashboard's session monitor via a
`/?session=<id>` navigation (the two are separate Vite entry points with no
shared router).

Centralizes the four system task type literals into one exported constant
set (SYSTEM_TASK_TYPES / isSystemTaskType) in blueprint-schema.ts, read by
the new rewrite, by validateFlow, and by the existing deletability checks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011zNBpMG8bTYfqUYqct8Xnu
Adds the tier-1 output value editor (str/int/float/bool/timestamp) to the
task edit panel, copies authored output values onto the derived task as
__sim_out__-prefixed params during Simulate, generalizes AgentLauncher so an
agent can claim every tool under its prefix via an optional canHandleTool/
invokeTool pair instead of one method per tool, and adds the SimulationAgent
stand-in (agent type "simulation") that completes a task from its simulated
outputs or holds indefinitely if none were authored. Only the tab that
pressed Simulate registers the stand-in (useSimulationStandIn, gated on the
sessionStorage marker from issue-sim-02).

Verified end-to-end against the docker-compose stack built from source
(real broker + real orchestrator), not only in unit tests; the procedure is
now documented in CONTEXT.md. Also fixes a pre-existing type-check failure
in blueprint-save.test.ts that was silently breaking `npm run build` (and
therefore the Docker frontend image) — discovered while bringing the stack
up for this issue's required integration test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VvvSiYv2ySDThhtJTXUBV
Adds tier 2 (list[T], repeating add/remove editor) and tier 3 (raw
COMPAS JSON textarea) to the simulated-output editor, covering dict,
any dotted class path, and any unrecognised/malformed/missing type —
no declared output type is left without an editor. Invalid JSON is
reported inline without touching the stored blueprint.

Extends the wire encoding to match: a new anyDataCodec.ts generalises
AnyData encode/decode beyond string/number/bool/ListData to plain
dicts (DictData) and COMPAS-envelope objects (FallbackData, so
compas_pb's decoder reconstructs the declared object rather than
handing back a plain dict).

Live testing against the docker-compose stack surfaced a sharper
requirement: BlueprintJsonSerializer.from_file parses the uploaded
blueprint file itself with compas.data's own JSON decoder, so a
{dtype, data} value embedded in a task param is already reconstructed
into a real COMPAS object before a session ever starts — and dispatched
using whichever native compas_pb message type that object has (e.g.
FrameData for compas.geometry.Frame), not FallbackData. SimulationAgent
therefore forwards simulated-output params as a raw AnyData passthrough
rather than decoding and re-encoding them, so it never needs to
recognise every such wire shape and, per ADR-0003, never constructs a
COMPAS object itself either way.
Adds a reserved __sim_use_real_agent__ param that deriveSimulationBlueprint
checks alongside system types when deciding whether to prefix a task's type,
plus a task-edit-panel toggle to set/clear it and a placeholder that replaces
the output editors for an opted-out task.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123bGqqHQZ58kbLKMVmEKpz
Generalises the SimulationAgent hold from issue-sim-03 (no authored output)
into a marked, releasable breakpoint with a session-monitor UI: toggle a
breakpoint per task from the graph's context menu, a step-through control to
break on every task, and a panel to edit a held task's output with the same
type-driven editors before continuing.

Fixes a real bug surfaced by live testing: the orchestrator qualifies every
task's wire id with its owning blueprint id (_create_global_id), which the
UI's plain per-blueprint task ids never matched — breakpoints would silently
never fire. SimulationAgent now takes the driving session's blueprint id
(threaded through simulation-session.ts's sessionStorage marker) and
translates wire ids back to plain ids before touching breakpoint/hold state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2zuiCLTE7MuUD9YfMA86D
Adds the two session-scoped simulation controls: a single artificial
per-task delay the stand-in applies after claiming and before completing
(so an unattended run stays watchable, never a duration prediction, and
never risks a redispatch since it happens post-claim), and a badge stating
whether the current tab is driving or merely watching a simulated session,
derived from the live graph's task types so it shows correctly on any tab.
Two independent bugs left a task held at a breakpoint with no way to
continue it, so the session hung there for good.

The session API serves blueprints through COMPAS json_dumps, which wraps
every Data object as {dtype, guid, data}. transformBlueprintToGraph
unwrapped the blueprint, its tasks and their params but passed the tasks'
inputs and outputs through still wrapped. Every earlier consumer coped by
re-deriving `io.data || io` itself (see TaskNode); SimulationBreakpointPanel
is the first to read name/type/value off them directly, so it saw undefined
for every value and type — no editor was seeded from the authored default,
every output degraded to the tier-3 JSON editor, and Continue stayed
disabled. Unwrapping in the transform gives GraphNode.inputs/outputs one
shape for every consumer instead of each one being a place to forget.

Separately, the ADR-0003 rule "a task with no authored output halts rather
than completing empty" was keyed off the absence of __sim_out__ params,
which conflated an output the author could have valued and didn't with a
task having no outputs to value at all. The second kind held forever behind
a Continue button that continueHeldTask then silently refused. The
orchestrator already sends the distinguishing fact as output_keys, so Task
now exposes it and the hold turns on "declares outputs and none authored".
A task declaring none completes with {}; breakpointed, it continues with {}.

Three existing tests asserted the old conflation by building assignment
messages with no output_keys at all, which no real orchestrator sends.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Simulate -> dashboard -> Edit -> Simulate is a round trip on one blueprint,
not a new blueprint per lap, but nothing supported going backwards:

- deriveSimulationBlueprintId appended unconditionally, so a blueprint that
  came back from a run turned into demo__sim__sim on the next Simulate. It
  is now idempotent, and stripSimulationBlueprintId unwinds however many
  suffixes have already stacked up.

- stripSimulationDerivation is the inverse of deriveSimulationBlueprint:
  source id, real task types, and none of the reserved __sim_out__ params.
  Lossless in the direction that matters, since the authored output values
  live on task.outputs — which is what __sim_out__ is derived from.

- rewriteTaskType checked for an already-rewritten type before it checked
  the opt-out flag and returned early, so ticking "use real agent" on a task
  carried back from a previous run was silently outranked by the prefix
  already on it. Opting out now wins and strips the prefix, so the flag can
  undo a rewrite rather than only prevent one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds an Edit action to the dashboard's blueprint preview, the mirror of
Simulate handing the tab the other way: authoring tool and dashboard are
separate page entry points, so a full navigation to
/author.html?blueprint=<id> is the handover. AuthorApp reads the id on
mount and loads it; file-open and this path now share one openBlueprint.
The id stays in the URL so a reload reopens the blueprint rather than
dropping the tab back to an empty canvas.

blueprint-load is the inverse of blueprint-save: GET /blueprints/{id}
returns COMPAS-wrapped JSON, while the authoring tool works in the flat
shape of blueprint-schema, so normalizeBlueprint unwraps it and drops the
nulls COMPAS fills in for absent values (a null type would render as a
declared type named "null"). It accepts both shapes, so a blueprint that
was exported and re-uploaded verbatim passes through untouched. The test
fixture was captured from the backend's own serialiser rather than guessed.

openBlueprint always presents the authored form, stripping the derivation
off a blueprint that came back from a simulated run — editing that as-is
would compound it on the next Simulate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chenkasirer chenkasirer closed this Aug 4, 2026
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