Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions backend/agents/chat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ skills:
- name: execute-code
- name: append-notebook-cell
- name: run-notebook-cell
- name: write-file
- name: edit-file
- name: run-file
- name: edit-notebook-cell
- name: delete-notebook-cell
- name: rerun-notebook-cell
- name: read-notebook
- name: delegate-task
- name: request-clarification
Expand Down Expand Up @@ -174,12 +180,32 @@ system: |
outputs freely. Soft conventions: `report.md` at the top level, `figures/`
for plots, `data/` for datasets, `models/` for trained models, `scripts/`
is managed by execute_code (auto-saved).
- The session workspace behaves like a real Python repo: it's the cwd of
every `execute-code` call and notebook cell, and `src/` is on `sys.path`.
Anything you write to `src/{name}.py` is importable from later calls
(`from features import build_X`). Use `src/` for reusable code; use
`scripts/` (auto-managed) as the one-shot audit log. Module names must
be plain Python identifiers — no hyphens, no step prefixes.
## Authoring vs running — use the right verb

Your session is a real Python repo. `/sessions/{sid}/src/` is on sys.path,
`/sessions/{sid}/` is your cwd. The skills split along the verbs you need:

- write-file(path, content) create or replace a file at `path`
- edit-file(path, mode, ...) change part of an existing file
- run-file(path) execute a file in the sandbox
- execute-code(code) one-shot throwaway Python; auto-saved to scripts/
- append-notebook-cell / run-notebook-cell / edit-notebook-cell /
delete-notebook-cell / rerun-notebook-cell for interactive work

**Rule of three:** before pasting code into a third execute-code call on
the same theme, stop and refactor. write-file(`src/{name}.py`, ...) the
shared body, then run-file it (or `from {name} import ...` in your next
cell). This holds for notebook cells too — `import` from `src/` works
inside a kernel cell.

**Edit, don't repaste.** If you'd be about to write-file a near-duplicate
of an existing module, edit-file the existing one. `scripts/` is the
audit log of what ran; `src/` is the library that survives. Keep `src/`
small and edited; let `scripts/` accumulate.

- Anything you write to `src/{name}.py` is importable from later calls
(`from features import build_X`). Module names must be plain Python
identifiers — no hyphens, no step prefixes.

## Environment
- `execute-code` runs Python in an isolated sandbox with pandas, numpy, matplotlib,
Expand Down
31 changes: 27 additions & 4 deletions backend/agents/data_prep.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ skills:
- name: execute-code
- name: append-notebook-cell
- name: run-notebook-cell
- name: write-file
- name: edit-file
- name: run-file
- name: edit-notebook-cell
- name: delete-notebook-cell
- name: rerun-notebook-cell
- name: read-notebook
- name: delegate-task
- name: request-clarification
Expand Down Expand Up @@ -65,11 +71,28 @@ system: |

ALWAYS create directories before writing: `os.makedirs('/data/sessions/{session_id}/data/', exist_ok=True)`.

## Workspace is a real Python repo
## Authoring vs running — use the right verb

The session directory is your cwd in every `execute-code` and notebook
cell, and `src/` is on `sys.path` with an auto-created `__init__.py`.
Any `.py` file you write under `src/` can be imported by later calls.
Your session is a real Python repo. `/sessions/{sid}/src/` is on sys.path,
`/sessions/{sid}/` is your cwd. The skills split along the verbs you need:

- write-file(path, content) create or replace a file at `path`
- edit-file(path, mode, ...) change part of an existing file
- run-file(path) execute a file in the sandbox
- execute-code(code) one-shot throwaway Python; auto-saved to scripts/
- append-notebook-cell / run-notebook-cell / edit-notebook-cell /
delete-notebook-cell / rerun-notebook-cell for interactive work

**Rule of three:** before pasting code into a third execute-code call on
the same theme, stop and refactor. write-file(`src/{name}.py`, ...) the
shared body, then run-file it (or `from {name} import ...` in your next
cell). This holds for notebook cells too — `import` from `src/` works
inside a kernel cell.

**Edit, don't repaste.** If you'd be about to write-file a near-duplicate
of an existing module, edit-file the existing one. `scripts/` is the
audit log of what ran; `src/` is the library that survives. Keep `src/`
small and edited; let `scripts/` accumulate.

- Factor reusable prep building blocks (raw loaders, cleaning passes,
a fitted `Pipeline.transform` wrapper, the encoders / scalers
Expand Down
34 changes: 30 additions & 4 deletions backend/agents/eda.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ skills:
- name: execute-code
- name: append-notebook-cell
- name: run-notebook-cell
- name: write-file
- name: edit-file
- name: run-file
- name: edit-notebook-cell
- name: delete-notebook-cell
- name: rerun-notebook-cell
- name: read-notebook
- name: delegate-task
- name: request-clarification
Expand Down Expand Up @@ -57,17 +63,37 @@ system: |

ALWAYS create directories before writing: `os.makedirs('/data/sessions/{session_id}/figures/', exist_ok=True)`.

## Workspace is a real Python repo
## Authoring vs running — use the right verb

The session directory is your cwd in every `execute-code` and notebook
cell, and `src/` is on `sys.path` with an auto-created `__init__.py`.
Any `.py` file you write under `src/` can be imported by later cells.
Your session is a real Python repo. `/sessions/{sid}/src/` is on sys.path,
`/sessions/{sid}/` is your cwd. The skills split along the verbs you need:

- write-file(path, content) create or replace a file at `path`
- edit-file(path, mode, ...) change part of an existing file
- run-file(path) execute a file in the sandbox
- execute-code(code) one-shot throwaway Python; auto-saved to scripts/
- append-notebook-cell / run-notebook-cell / edit-notebook-cell /
delete-notebook-cell / rerun-notebook-cell for interactive work

**Rule of three:** before pasting code into a third execute-code call on
the same theme, stop and refactor. write-file(`src/{name}.py`, ...) the
shared body, then run-file it (or `from {name} import ...` in your next
cell). This holds for notebook cells too — `import` from `src/` works
inside a kernel cell.

**Edit, don't repaste.** If you'd be about to write-file a near-duplicate
of an existing module, edit-file the existing one. `scripts/` is the
audit log of what ran; `src/` is the library that survives. Keep `src/`
small and edited; let `scripts/` accumulate.

- For EDA you'll mostly stay in notebooks (kernel state already
persists across cells), but the `src/` convention is useful when
you want to promote a reusable helper out of a notebook into a
module — e.g. `src/loaders.py` with `load_raw()` — so data_prep
can `from loaders import load_raw` instead of rewriting it.
- When you find yourself pasting the same helper into a third notebook
cell, write-file it to `src/{name}.py` and import it in the next
cell — the kernel will find it without restart.
- Module names must be plain Python identifiers (no hyphens, no
step prefixes). The auto-saved per-call scripts under `scripts/`
are step-prefixed by design and are NOT importable.
Expand Down
31 changes: 27 additions & 4 deletions backend/agents/feature_eng.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ skills:
- name: execute-code
- name: append-notebook-cell
- name: run-notebook-cell
- name: write-file
- name: edit-file
- name: run-file
- name: edit-notebook-cell
- name: delete-notebook-cell
- name: rerun-notebook-cell
- name: read-notebook
- name: delegate-task
- name: request-clarification
Expand Down Expand Up @@ -55,11 +61,28 @@ system: |

ALWAYS create directories first: `os.makedirs('/data/sessions/{session_id}/features/', exist_ok=True)`.

## Workspace is a real Python repo
## Authoring vs running — use the right verb

The session directory is your cwd in every `execute-code` and notebook
cell, and `src/` is on `sys.path` with an auto-created `__init__.py`.
Any `.py` file you write under `src/` can be imported by later calls.
Your session is a real Python repo. `/sessions/{sid}/src/` is on sys.path,
`/sessions/{sid}/` is your cwd. The skills split along the verbs you need:

- write-file(path, content) create or replace a file at `path`
- edit-file(path, mode, ...) change part of an existing file
- run-file(path) execute a file in the sandbox
- execute-code(code) one-shot throwaway Python; auto-saved to scripts/
- append-notebook-cell / run-notebook-cell / edit-notebook-cell /
delete-notebook-cell / rerun-notebook-cell for interactive work

**Rule of three:** before pasting code into a third execute-code call on
the same theme, stop and refactor. write-file(`src/{name}.py`, ...) the
shared body, then run-file it (or `from {name} import ...` in your next
cell). This holds for notebook cells too — `import` from `src/` works
inside a kernel cell.

**Edit, don't repaste.** If you'd be about to write-file a near-duplicate
of an existing module, edit-file the existing one. `scripts/` is the
audit log of what ran; `src/` is the library that survives. Keep `src/`
small and edited; let `scripts/` accumulate.

- Factor reusable feature builders (date-part extractors, target
encoders, interaction-feature helpers) into `src/features.py` and
Expand Down
36 changes: 32 additions & 4 deletions backend/agents/orchestrator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ skills:
- name: execute-code
- name: append-notebook-cell
- name: run-notebook-cell
- name: write-file
- name: edit-file
- name: run-file
- name: edit-notebook-cell
- name: delete-notebook-cell
- name: rerun-notebook-cell
- name: read-notebook
- name: delegate-task
- name: request-clarification
Expand Down Expand Up @@ -215,10 +221,32 @@ system: |
`report.md`, `data/`, `figures/`, `models/`, `features/`)
- Reports from previous agents in this session are injected into each
sub-agent's system prompt via `## Prior context`
- The workspace behaves like a Python repo: it's the cwd of every
`execute-code` call and notebook cell, and `src/` is on `sys.path`.
Sub-agents should factor reusable helpers into `src/{name}.py` and
`import` them across calls rather than re-pasting boilerplate.
## Authoring vs running — use the right verb

Your session is a real Python repo. `/sessions/{sid}/src/` is on sys.path,
`/sessions/{sid}/` is your cwd. The skills split along the verbs you need:

- write-file(path, content) create or replace a file at `path`
- edit-file(path, mode, ...) change part of an existing file
- run-file(path) execute a file in the sandbox
- execute-code(code) one-shot throwaway Python; auto-saved to scripts/
- append-notebook-cell / run-notebook-cell / edit-notebook-cell /
delete-notebook-cell / rerun-notebook-cell for interactive work

**Rule of three:** before pasting code into a third execute-code call on
the same theme, stop and refactor. write-file(`src/{name}.py`, ...) the
shared body, then run-file it (or `from {name} import ...` in your next
cell). This holds for notebook cells too — `import` from `src/` works
inside a kernel cell.

**Edit, don't repaste.** If you'd be about to write-file a near-duplicate
of an existing module, edit-file the existing one. `scripts/` is the
audit log of what ran; `src/` is the library that survives. Keep `src/`
small and edited; let `scripts/` accumulate.

- Sub-agents share this same surface — when you delegate, they can
write-file reusable helpers into `src/{name}.py` and `import` them
across calls rather than re-pasting boilerplate.

## Collaboration & skepticism
- Be skeptical. Do not assume — verify. If a user request is ambiguous, or if
Expand Down
32 changes: 27 additions & 5 deletions backend/agents/trainer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ skills:
- name: execute-code
- name: append-notebook-cell
- name: run-notebook-cell
- name: write-file
- name: edit-file
- name: run-file
- name: edit-notebook-cell
- name: delete-notebook-cell
- name: rerun-notebook-cell
- name: read-notebook
- name: delegate-task
- name: request-clarification
Expand Down Expand Up @@ -68,12 +74,28 @@ system: |

ALWAYS create directories before writing: `os.makedirs('/data/sessions/{session_id}/models/', exist_ok=True)`.

## Workspace is a real Python repo
## Authoring vs running — use the right verb

The session directory is your cwd in every `execute-code` and notebook
cell. Any `.py` file you write under `src/` can be imported by later
calls — `src/` is on `sys.path` and `src/__init__.py` is created for
you on first boot.
Your session is a real Python repo. `/sessions/{sid}/src/` is on sys.path,
`/sessions/{sid}/` is your cwd. The skills split along the verbs you need:

- write-file(path, content) create or replace a file at `path`
- edit-file(path, mode, ...) change part of an existing file
- run-file(path) execute a file in the sandbox
- execute-code(code) one-shot throwaway Python; auto-saved to scripts/
- append-notebook-cell / run-notebook-cell / edit-notebook-cell /
delete-notebook-cell / rerun-notebook-cell for interactive work

**Rule of three:** before pasting code into a third execute-code call on
the same theme, stop and refactor. write-file(`src/{name}.py`, ...) the
shared body, then run-file it (or `from {name} import ...` in your next
cell). This holds for notebook cells too — `import` from `src/` works
inside a kernel cell.

**Edit, don't repaste.** If you'd be about to write-file a near-duplicate
of an existing module, edit-file the existing one. `scripts/` is the
audit log of what ran; `src/` is the library that survives. Keep `src/`
small and edited; let `scripts/` accumulate.

- Factor reusable training-loop building blocks (data loading,
evaluator helpers, the `objective(trial)` function for Optuna,
Expand Down
76 changes: 76 additions & 0 deletions backend/services/notebook_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,82 @@ async def append_cell(
}


async def edit_cell(
session_id: str,
name: str,
cell_id: str,
source: str,
cell_type: Optional[str] = None,
) -> dict:
"""Replace a cell's source in place. Outputs are preserved by default —
same behavior as `apply_source_update`'s output carry-over for the
frontend HTTP PUT path. This is the per-cell agent surface.
"""
if cell_type is not None and cell_type not in ("code", "markdown"):
raise ValueError(f"cell_type must be 'code' or 'markdown', got {cell_type!r}")

name = sanitize_name(name)
key = (session_id, name)
nb = await load(session_id, name)
if nb is None:
raise ValueError(f"Notebook '{name}' not found")

async with _lock(key):
cell = next((c for c in nb.cells if c.get("id") == cell_id), None)
if cell is None:
raise ValueError(f"Cell '{cell_id}' not found in notebook '{name}'")
cell["source"] = source
if cell_type is not None and cell_type != cell.get("cell_type"):
cell["cell_type"] = cell_type
if cell_type == "code":
cell.setdefault("outputs", [])
cell["execution_count"] = None
else:
cell.pop("outputs", None)
cell.pop("execution_count", None)
_cache[key] = nb

await save(session_id, name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Notebook saves can reorder

When an edit or deletion overlaps another notebook mutation or kernel completion, each operation releases the notebook lock before its volume upload finishes, allowing an older snapshot to land last and restore a deleted cell or discard newer source and output changes.

Fix in Claude Code

return {
"notebook_name": name,
"notebook_path": notebook_path(session_id, name),
"cell_id": cell_id,
"source_len": len(source),
"total_cells": len(nb.cells),
}


async def delete_cell(
session_id: str,
name: str,
cell_id: str,
) -> dict:
"""Remove a cell from the notebook and persist."""
name = sanitize_name(name)
key = (session_id, name)
nb = await load(session_id, name)
if nb is None:
raise ValueError(f"Notebook '{name}' not found")

async with _lock(key):
idx = next(
(i for i, c in enumerate(nb.cells) if c.get("id") == cell_id),
None,
)
if idx is None:
raise ValueError(f"Cell '{cell_id}' not found in notebook '{name}'")
nb.cells.pop(idx)
_cache[key] = nb

await save(session_id, name)
return {
"notebook_name": name,
"notebook_path": notebook_path(session_id, name),
"cell_id": cell_id,
"remaining_cells": len(nb.cells),
}


async def on_cell_event(
session_id: str,
name: str,
Expand Down
Loading