diff --git a/mkdocs/docs/concepts/presets.md b/mkdocs/docs/concepts/presets.md
index e091dd28f..36a639622 100644
--- a/mkdocs/docs/concepts/presets.md
+++ b/mkdocs/docs/concepts/presets.md
@@ -122,7 +122,7 @@ Alternatively, pass `--fleet` to `dstack apply`.
=== "Base"
- Set `base` to let the creation agent select any compatible variant of the base model, including a different precision, quantization, or trusted fork.
+ Set `base` to let the agent select any compatible variant of the base model, including a different precision, quantization, or trusted fork.
```yaml
base: Qwen/Qwen2.5-7B-Instruct
@@ -240,6 +240,24 @@ Submit the run dsv4-flash? [y/n]: y
## Manage presets
+### Watch presets
+
+While a preset is being created, you can watch the progress of its trials and what the agent is doing.
+
+The `dstack preset logs` command shows the progress log: one line per milestone, such as a trial finishing or the final service being verified. Pass `-f` to follow a running creation:
+
+
+
+```shell
+$ dstack preset logs -f c83375b4
+```
+
+
+
+### Traces
+
+The agent subprocess writes real-time traces to `~/.dstack/presets//trace.jsonl`: the agent's messages and every tool call with its result. Traces are the main way to analyze a session in depth — see [Protips](#protips).
+
### List presets
Use `dstack preset` to list presets:
@@ -290,19 +308,14 @@ $ dstack preset delete c83375b4
!!! info "Reference"
For command options and agent settings, see the [`dstack preset` CLI reference](../reference/cli/dstack/preset.md).
-## Troubleshooting
+## Protips
-To trace the agent's activity, pass `--debug` to `dstack apply`:
-
-
+Under the hood, presets run an agent as a subprocess, using the local `claude` CLI. This process writes a real-time trace to `~/.dstack/presets//trace.jsonl`. The subprocess is launched with a built-in harness: how to run trials, submit runs, benchmark, verify presets, and use `dstack`.
-```shell
-$ dstack apply -f preset.dstack.yml --debug
-```
-
-
+At the same time, it's recommended to create presets using your own agent — either via a CLI such as Claude Code, or inside your IDE. Your agent helps you design the preset configuration, formulate hypotheses, and — most importantly — analyze the session's traces as well as the trial results (stored under `~/.dstack/presets//trials//trial.json`), to decide what the next session can be and what instructions to give it via `prompt`.
-The trace is written to `~/.dstack/presets//trace.jsonl` while the session runs. It contains the agent's messages and every tool call with its result.
+> To help your agent use `dstack` and presets, install the [`dstack`](https://skills.sh/dstackai/dstack/dstack)
+> and [`dstack-presets`](https://skills.sh/dstackai/dstack/dstack-presets) skills with `npx skills add dstackai/dstack`.
## Limitations
diff --git a/mkdocs/docs/reference/cli/dstack/preset.md b/mkdocs/docs/reference/cli/dstack/preset.md
index 3aacffe73..bff3a597d 100644
--- a/mkdocs/docs/reference/cli/dstack/preset.md
+++ b/mkdocs/docs/reference/cli/dstack/preset.md
@@ -49,9 +49,9 @@ Preset creation uses the existing `claude` login unless
| `DSTACK_AGENT_CLAUDE_EFFORT` | Claude effort level: `low`, `medium`, `high`, `xhigh`, or `max`. If unset, the `claude` CLI default is used. |
Agent progress is written to `agent.log` under `~/.dstack/presets//`,
-alongside the effective configuration (`preset.dstack.yml`) and the recorded
-trials (`trials.jsonl`). Pass `--debug` to also save the agent prompt
-(`prompt.md`) and raw trace (`trace.jsonl`).
+alongside the effective configuration (`preset.dstack.yml`), the recorded
+trials, the agent prompt (`prompt.md`), and the real-time trace
+(`trace.jsonl`).
## dstack preset logs
diff --git a/skills/dstack-presets/SKILL.md b/skills/dstack-presets/SKILL.md
new file mode 100644
index 000000000..bae536306
--- /dev/null
+++ b/skills/dstack-presets/SKILL.md
@@ -0,0 +1,29 @@
+---
+name: dstack-presets
+description: |
+ Create and manage dstack presets: a toolkit that streamlines model inference optimization with agents, and a portable preset format. Use together with the dstack skill, and only when the user explicitly asks to create a preset or manage existing presets, not for deploying or serving a model.
+---
+
+# dstack Presets
+
+Use `/dstack` for CLI commands, YAML fields, apply behavior, fleets, and other
+dstack syntax. This skill covers creating and managing presets.
+
+## Overview
+
+Presets offer two things: a toolkit that streamlines model inference optimization using agents, and a portable format that deploys the final preset to any cloud, Kubernetes cluster, or bare-metal fleet. A preset holds the serving configuration that produced the result, the benchmark it reached, and the exact hardware it was verified on.
+
+Presets are used for three kinds of work: finding an optimized baseline, optimizing through patching source code, and supporting new hardware.
+
+**When to use this skill:**
+- The user explicitly asks to create a preset, or to optimize model inference via a preset
+- Managing already created presets: watching sessions, listing, exporting, and deleting them via `dstack preset` commands
+
+**When NOT to use this skill:**
+- Deploying or serving a model: use a service instead (see the `dstack` skill)
+
+## How to use presets
+
+Follow the [presets documentation](https://dstack.ai/docs/concepts/presets.md).
+
+[Configuration reference](https://dstack.ai/docs/reference/dstack.yml/preset.md) | [CLI reference](https://dstack.ai/docs/reference/cli/dstack/preset.md)
diff --git a/src/dstack/_internal/cli/models/preset_agent.py b/src/dstack/_internal/cli/models/preset_agent.py
index abc06e099..24629d1a4 100644
--- a/src/dstack/_internal/cli/models/preset_agent.py
+++ b/src/dstack/_internal/cli/models/preset_agent.py
@@ -118,7 +118,6 @@ class PresetSessionState(CoreModel):
trials_num: Optional[int]
previous: list[str]
created_at: datetime
- debug: bool
status: PresetSessionStatus
# None is a detached session.
owner: Optional[PresetSessionProcess]
diff --git a/src/dstack/_internal/cli/services/configurators/preset.py b/src/dstack/_internal/cli/services/configurators/preset.py
index 59437e31c..620092b0a 100644
--- a/src/dstack/_internal/cli/services/configurators/preset.py
+++ b/src/dstack/_internal/cli/services/configurators/preset.py
@@ -66,7 +66,6 @@ def apply_configuration(
configuration=conf,
store=store,
keep_service=configurator_args.keep_service,
- debug=configurator_args.debug,
user_prompt=user_prompt,
allowed_fleets=allowed_fleets,
previous=previous,
@@ -131,11 +130,6 @@ def register_creation_args(parser: ArgsParser) -> None:
metavar="N",
help="The number of benchmarked trials before the best one is promoted",
)
- parser.add_argument(
- "--debug",
- action="store_true",
- help="Save the agent prompt and raw trace",
- )
parser.add_argument(
"--previous",
action="append",
diff --git a/src/dstack/_internal/cli/services/presets/agent.py b/src/dstack/_internal/cli/services/presets/agent.py
index f9e4a1934..a8c436675 100644
--- a/src/dstack/_internal/cli/services/presets/agent.py
+++ b/src/dstack/_internal/cli/services/presets/agent.py
@@ -375,7 +375,7 @@ def _prepare_subprocess_command(command: list[str]) -> list[str]:
return [comspec, "/d", "/s", "/c", subprocess.list2cmdline(command)]
-def _write_debug_trace(
+def _write_trace(
session: PresetSession,
*,
stream_name: Literal["stdout", "stderr"],
@@ -523,7 +523,7 @@ async def _read_process_stream(
redacted_values: Sequence[str],
session: PresetSession,
) -> PresetAgentProcessOutput:
- # stderr feeds the debug trace and advances the persisted offset, but only
+ # stderr feeds the trace and advances the persisted offset, but only
# stdout can carry the report.
parse_result = stream_name == "stdout"
output = PresetAgentProcessOutput()
@@ -532,13 +532,12 @@ async def _read_process_stream(
if not line:
return output
text = line.decode(errors="replace")
- if session.debug:
- _write_debug_trace(
- session,
- stream_name=stream_name,
- text=text,
- redacted_values=redacted_values,
- )
+ _write_trace(
+ session,
+ stream_name=stream_name,
+ text=text,
+ redacted_values=redacted_values,
+ )
if not parse_result:
continue
try:
diff --git a/src/dstack/_internal/cli/services/presets/create.py b/src/dstack/_internal/cli/services/presets/create.py
index b15c1ad15..02fbfd92b 100644
--- a/src/dstack/_internal/cli/services/presets/create.py
+++ b/src/dstack/_internal/cli/services/presets/create.py
@@ -400,7 +400,6 @@ def create_preset(
store: PresetStore,
keep_service: bool = False,
build_name: Optional[str] = None,
- debug: bool = False,
resume_session: Optional[PresetSession] = None,
user_prompt: Optional[str] = None,
allowed_fleets: Optional[tuple[str, ...]] = None,
@@ -409,7 +408,6 @@ def create_preset(
session = resume_session or create_preset_session(
configuration,
previous=tuple(session.preset_id for session in previous),
- debug=debug,
)
try:
resolved_configuration = _resolve_preset_env(configuration)
@@ -629,10 +627,9 @@ async def _create_preset(
# A second, persistent copy: the workspace above is deleted with the run,
# while the listing and `--previous` read constraints from the session dir.
session.write_constraints(constraints_text)
- if session.debug:
- session.write_prompt(prompt)
- if setup.auth is not None:
- session.write_agent_info(setup.auth)
+ session.write_prompt(prompt)
+ if setup.auth is not None:
+ session.write_agent_info(setup.auth)
try:
if mode == "attach":
process_output = await attach_preset_agent(
@@ -685,12 +682,11 @@ async def _create_preset(
interrupted = True
raise
finally:
- if session.debug:
- _save_final_report_copy(
- workspace=setup.workspace,
- session=session,
- redacted_values=redacted_values,
- )
+ _save_final_report_copy(
+ workspace=setup.workspace,
+ session=session,
+ redacted_values=redacted_values,
+ )
if not interrupted:
keep_final_service = keep_service and creation_succeeded
try:
@@ -1009,8 +1005,7 @@ async def _cleanup_runs(
pending.remove(name)
if pending:
await asyncio.sleep(2)
- if session.debug:
- print_preset_progress("All preset creation runs stopped.", session=session)
+ print_preset_progress("All preset creation runs stopped.", session=session)
def _load_submitted_run_names(path: Path) -> list[str]:
diff --git a/src/dstack/_internal/cli/services/presets/session.py b/src/dstack/_internal/cli/services/presets/session.py
index fb9ace9f8..6c0588919 100644
--- a/src/dstack/_internal/cli/services/presets/session.py
+++ b/src/dstack/_internal/cli/services/presets/session.py
@@ -56,7 +56,6 @@ class SessionBusyError(CLIError):
@dataclass
class PresetSession:
path: Path
- debug: bool
preset_id: str
# Background reconcile sets this False so finalizing a detached session stays
# silent on the read command; agent.log is written regardless.
@@ -269,7 +268,6 @@ def create_preset_session(
configuration: PresetConfiguration,
*,
previous: Sequence[str],
- debug: bool,
) -> PresetSession:
if configuration.name is None:
raise CLIError("The service name is required to save agent output")
@@ -286,7 +284,8 @@ def create_preset_session(
continue
break
_write_private_text(path / "agent.log", "")
- session = PresetSession(path=path, debug=debug, preset_id=preset_id)
+ _write_private_text(path / "trace.jsonl", "")
+ session = PresetSession(path=path, preset_id=preset_id)
session.write_state(
PresetSessionState(
id=preset_id,
@@ -295,7 +294,6 @@ def create_preset_session(
trials_num=configuration.trials,
previous=list(previous),
created_at=datetime.now(timezone.utc),
- debug=debug,
status="running",
owner=_current_process(),
run=None,
@@ -311,8 +309,6 @@ def create_preset_session(
path / "preset.dstack.yml",
yaml.safe_dump(record, sort_keys=False),
)
- if debug:
- _write_private_text(path / "trace.jsonl", "")
except OSError as e:
if path is not None:
shutil.rmtree(path, ignore_errors=True)
@@ -322,7 +318,7 @@ def create_preset_session(
def load_resumable_session(preset_id: str) -> PresetSession:
path = get_presets_dir() / preset_id
- session = PresetSession(path=path, debug=False, preset_id=preset_id)
+ session = PresetSession(path=path, preset_id=preset_id)
state = session.read_state()
if not path.is_dir() or state is None:
raise CLIError(f"Unknown preset: {preset_id}")
@@ -337,7 +333,6 @@ def load_resumable_session(preset_id: str) -> PresetSession:
)
if state.run is None or state.run.claude_session_id is None:
raise CLIError(f"Preset {preset_id} creation stopped before it started; create a new one")
- session.debug = state.debug
return session
@@ -368,7 +363,7 @@ def session_process_alive(state: PresetSessionState) -> bool:
def load_attachable_session(preset_id: str) -> PresetSession:
path = get_presets_dir() / preset_id
- session = PresetSession(path=path, debug=False, preset_id=preset_id)
+ session = PresetSession(path=path, preset_id=preset_id)
state = session.read_state()
if not path.is_dir() or state is None:
raise CLIError(f"Unknown preset: {preset_id}")
@@ -387,13 +382,12 @@ def load_attachable_session(preset_id: str) -> PresetSession:
f"Preset {preset_id} is already being followed by another CLI (pid {owner.pid});"
f" stop or detach it there with Ctrl+C"
)
- session.debug = state.debug
return session
def load_preset_session(preset_id: str) -> PresetSession:
path = get_presets_dir() / preset_id
- session = PresetSession(path=path, debug=False, preset_id=preset_id)
+ session = PresetSession(path=path, preset_id=preset_id)
if not path.is_dir() or session.read_state() is None:
raise CLIError(f"Unknown preset: {preset_id}")
return session
@@ -476,7 +470,7 @@ def iter_preset_sessions() -> Iterator[PresetSession]:
return
for path in sorted(root.iterdir()):
if path.is_dir() and not path.name.startswith((".", "models--")):
- yield PresetSession(path=path, debug=False, preset_id=path.name)
+ yield PresetSession(path=path, preset_id=path.name)
def find_session_name_claims(name: str) -> list[PresetSession]:
diff --git a/src/tests/_internal/cli/commands/test_preset.py b/src/tests/_internal/cli/commands/test_preset.py
index 26cda776b..326a56741 100644
--- a/src/tests/_internal/cli/commands/test_preset.py
+++ b/src/tests/_internal/cli/commands/test_preset.py
@@ -485,7 +485,6 @@ def test_merges_profile_configuration_and_cli_args(self, tmp_path):
"0.75",
"--fleet",
"cli-fleet",
- "--debug",
],
home_dir=tmp_path,
repo_dir=tmp_path,
@@ -499,7 +498,6 @@ def test_merges_profile_configuration_and_cli_args(self, tmp_path):
assert configuration.max_price == 0.75
assert configuration.spot_policy.value == "spot"
assert [fleet.format() for fleet in configuration.fleets] == ["cli-fleet"]
- assert create.call_args.kwargs["debug"] is True
def test_create_detaches_the_name_from_the_old_preset(self, tmp_path):
preset = get_preset().model_copy(update={"name": "qwen"})
@@ -630,7 +628,6 @@ def test_accepts_creation_and_profile_arguments(self, tmp_path):
"7",
"--backend",
"gcp",
- "--debug",
],
home_dir=tmp_path,
repo_dir=tmp_path,
@@ -641,7 +638,6 @@ def test_accepts_creation_and_profile_arguments(self, tmp_path):
assert configuration.name == "cli-name"
assert configuration.trials == 7
assert configuration.backends == ["gcp"]
- assert create.call_args.kwargs["debug"] is True
def test_rejects_detach(self, tmp_path, capsys):
configuration_path = self._write_configuration(tmp_path)
diff --git a/src/tests/_internal/cli/common.py b/src/tests/_internal/cli/common.py
index 081eb8018..182c8d6a2 100644
--- a/src/tests/_internal/cli/common.py
+++ b/src/tests/_internal/cli/common.py
@@ -210,7 +210,6 @@ def get_session_state(**overrides: Any) -> PresetSessionState:
"trials_num": None,
"previous": [],
"created_at": datetime(2026, 1, 2, 3, 4, tzinfo=timezone.utc),
- "debug": False,
"status": "running",
"owner": None,
"run": None,
diff --git a/src/tests/_internal/cli/services/presets/test_agent.py b/src/tests/_internal/cli/services/presets/test_agent.py
index 742a85373..9182b92b4 100644
--- a/src/tests/_internal/cli/services/presets/test_agent.py
+++ b/src/tests/_internal/cli/services/presets/test_agent.py
@@ -177,7 +177,7 @@ def test_detects_known_secret_in_generated_artifact(self):
def _session_workspace(tmp_path):
session_dir = tmp_path / "session-under-test"
session_dir.mkdir()
- session = PresetSession(path=session_dir, debug=False, preset_id="abcd1234")
+ session = PresetSession(path=session_dir, preset_id="abcd1234")
session.write_state(get_session_state(id="abcd1234"))
workspace, _ = create_agent_workspace(session)
return workspace
@@ -199,7 +199,7 @@ def _home(self, tmp_path, monkeypatch) -> None:
def test_creates_private_session_with_log_and_manifest(self, tmp_path, monkeypatch, capsys):
self._home(tmp_path, monkeypatch)
- session = create_preset_session(self._configuration(), previous=(), debug=False)
+ session = create_preset_session(self._configuration(), previous=())
assert session.path.parent == tmp_path / ".dstack" / "presets"
assert session.path.name == session.preset_id
@@ -208,6 +208,7 @@ def test_creates_private_session_with_log_and_manifest(self, tmp_path, monkeypat
"agent.log",
"session.json",
"preset.dstack.yml",
+ "trace.jsonl",
}
state = json.loads((session.path / "session.json").read_text())
assert state["id"] == session.preset_id
@@ -222,26 +223,20 @@ def test_creates_private_session_with_log_and_manifest(self, tmp_path, monkeypat
assert session.path.stat().st_mode & 0o777 == 0o700
assert session.log_path.stat().st_mode & 0o777 == 0o600
- def test_debug_session_saves_scrubbed_configuration_and_trace(self, tmp_path, monkeypatch):
+ def test_session_saves_scrubbed_configuration(self, tmp_path, monkeypatch):
self._home(tmp_path, monkeypatch)
- debug_session = create_preset_session(self._configuration(), previous=(), debug=True)
+ session = create_preset_session(self._configuration(), previous=())
- data = yaml.safe_load((debug_session.path / "preset.dstack.yml").read_text())
- assert {path.name for path in debug_session.path.iterdir()} == {
- "agent.log",
- "preset.dstack.yml",
- "session.json",
- "trace.jsonl",
- }
+ data = yaml.safe_load((session.path / "preset.dstack.yml").read_text())
assert data["max_price"] == 0.5
assert data["env"] == ["HF_TOKEN", "TOKENIZERS_PARALLELISM"]
- assert "false" not in (debug_session.path / "preset.dstack.yml").read_text()
+ assert "false" not in (session.path / "preset.dstack.yml").read_text()
@pytest.mark.parametrize("status", ["success", "failed"])
def test_finish_records_terminal_status(self, tmp_path, monkeypatch, status):
self._home(tmp_path, monkeypatch)
- session = create_preset_session(self._configuration(), previous=(), debug=False)
+ session = create_preset_session(self._configuration(), previous=())
finished_path = session.finish(status)
@@ -254,7 +249,6 @@ def test_finish_writes_status_in_place(self, tmp_path):
(session_dir / "agent.log").touch()
session = PresetSession(
path=session_dir,
- debug=False,
preset_id="ab12cd34",
)
session.write_state(get_session_state())
@@ -274,7 +268,7 @@ def test_reports_invalid_existing_parent(self, tmp_path, monkeypatch):
with pytest.raises(CLIError, match="Could not create agent output"):
create_preset_session(
- PresetConfiguration(name="qwen", base="Qwen/Qwen3.5-27B"), previous=(), debug=False
+ PresetConfiguration(name="qwen", base="Qwen/Qwen3.5-27B"), previous=()
)
def test_log_write_failure_warns_once(self, tmp_path, capsys):
@@ -283,7 +277,6 @@ def test_log_write_failure_warns_once(self, tmp_path, capsys):
(path / "agent.log").touch()
session = PresetSession(
path=path,
- debug=False,
preset_id="ab12cd34",
)
shutil.rmtree(path)
@@ -326,13 +319,12 @@ async def test_sends_prompt_and_redacts_raw_output(self, tmp_path, monkeypatch,
)
workspace = PresetAgentWorkspace(path=tmp_path, dstack_home=tmp_path / "home")
- session_path = tmp_path / "debug-running"
+ session_path = tmp_path / "session-running"
session_path.mkdir()
(session_path / "agent.log").touch()
(session_path / "trace.jsonl").touch()
session = PresetSession(
path=session_path,
- debug=True,
preset_id="ab12cd34",
)
output = await run_preset_agent(
@@ -385,7 +377,7 @@ async def test_mirrors_trial_and_service_records_into_the_session(self, tmp_path
session_path = tmp_path / "session"
session_path.mkdir()
(session_path / "agent.log").touch()
- session = PresetSession(path=session_path, debug=False, preset_id="ab12cd34")
+ session = PresetSession(path=session_path, preset_id="ab12cd34")
output = await run_preset_agent(
prompt="p",
@@ -434,7 +426,6 @@ async def test_accepts_stream_event_larger_than_64_kib(self, tmp_path, monkeypat
redacted_values=(),
session=PresetSession(
path=session_path,
- debug=False,
preset_id="ab12cd34",
),
)
@@ -450,7 +441,6 @@ def test_progress_stream_prints_only_redacted_messages(self, tmp_path, capsys):
(session_path / "agent.log").touch()
session = PresetSession(
path=session_path,
- debug=False,
preset_id="ab12cd34",
)
@@ -637,7 +627,7 @@ def test_writes_model_params_and_auth(self, tmp_path, monkeypatch):
)
session_dir = tmp_path / "session"
session_dir.mkdir()
- session = PresetSession(path=session_dir, debug=True, preset_id="ab12cd34")
+ session = PresetSession(path=session_dir, preset_id="ab12cd34")
session.write_agent_info(
ClaudeAuth(api_key=None, executable="claude", effort=None, model="claude-opus-4-8")
@@ -654,7 +644,7 @@ def test_writes_model_params_and_auth(self, tmp_path, monkeypatch):
def _offsets(tmp_path):
session_dir = tmp_path / "offsets-session"
session_dir.mkdir(exist_ok=True)
- return open_session_offsets(PresetSession(path=session_dir, debug=False, preset_id="offsets0"))
+ return open_session_offsets(PresetSession(path=session_dir, preset_id="offsets0"))
def _subprocess_env() -> dict[str, str]:
@@ -679,7 +669,6 @@ def _agent_setup(tmp_path):
(session_path / "agent.log").touch()
session = PresetSession(
path=session_path,
- debug=False,
preset_id="ab12cd34",
)
session.write_state(get_session_state())
@@ -870,7 +859,7 @@ class TestWorkspaceLifecycle:
def _session(self, tmp_path):
session_dir = tmp_path / "sessions" / "ab12cd34"
session_dir.mkdir(parents=True)
- session = PresetSession(path=session_dir, debug=False, preset_id="ab12cd34")
+ session = PresetSession(path=session_dir, preset_id="ab12cd34")
session.write_state(get_session_state())
return session
@@ -976,7 +965,7 @@ def test_reads_a_pre_0_22_flat_session_file(self, tmp_path):
session_dir = tmp_path / "30a012bf"
session_dir.mkdir()
(session_dir / "session.json").write_text(json.dumps(flat))
- session = PresetSession(path=session_dir, debug=False, preset_id="30a012bf")
+ session = PresetSession(path=session_dir, preset_id="30a012bf")
state = session.read_state()
@@ -1012,7 +1001,6 @@ def test_loads_interrupted_session(self, tmp_path, monkeypatch):
"id": "ab12cd34",
"status": "interrupted",
"run": get_session_run(claude_session_id="sid-1"),
- "debug": True,
"created_at": "2026-07-20T10:00:00Z",
},
)
@@ -1020,7 +1008,6 @@ def test_loads_interrupted_session(self, tmp_path, monkeypatch):
session = load_resumable_session("ab12cd34")
assert session.preset_id == "ab12cd34"
- assert session.debug is True
def test_treats_dead_running_session_as_resumable(self, tmp_path, monkeypatch):
self._write_session(
@@ -1246,7 +1233,7 @@ def test_detach_keeps_the_agent_and_stop_terminates_it(self, tmp_path, monkeypat
session_dir = tmp_path / "ab12cd34"
session_dir.mkdir()
(session_dir / "agent.log").touch()
- session = PresetSession(path=session_dir, debug=False, preset_id="ab12cd34")
+ session = PresetSession(path=session_dir, preset_id="ab12cd34")
session.write_state(get_session_state())
agent = subprocess.Popen(
[sys.executable, "-c", "import time; time.sleep(300)"], start_new_session=True
diff --git a/src/tests/_internal/cli/services/presets/test_create.py b/src/tests/_internal/cli/services/presets/test_create.py
index aa21760f6..683fb55a9 100644
--- a/src/tests/_internal/cli/services/presets/test_create.py
+++ b/src/tests/_internal/cli/services/presets/test_create.py
@@ -133,7 +133,7 @@ def creation_context(tmp_path, monkeypatch):
class TestCreatePreset:
- def test_saves_agent_log_without_debug(self, tmp_path, monkeypatch):
+ def test_saves_agent_log_and_trace(self, tmp_path, monkeypatch):
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
preset = get_preset()
@@ -167,13 +167,14 @@ async def create(**kwargs):
"agent.log",
"session.json",
"preset.dstack.yml",
+ "trace.jsonl",
}
state = json.loads((paths[0] / "session.json").read_text())
assert state["status"] == "success"
assert state["id"] == paths[0].name
assert "testing preset" in (paths[0] / "agent.log").read_text()
- def test_debug_finalization_error_does_not_mask_success(self, tmp_path, monkeypatch, capsys):
+ def test_finalization_error_does_not_mask_success(self, tmp_path, monkeypatch, capsys):
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
monkeypatch.setenv("HF_TOKEN", "hf-secret")
@@ -211,7 +212,6 @@ def fail_finish(self, preset_id=None):
env=["HF_TOKEN", "TOKENIZERS_PARALLELISM=false"],
),
store=PresetStore(tmp_path / "presets"),
- debug=True,
)
paths = _session_dirs(tmp_path)
@@ -228,7 +228,7 @@ def fail_finish(self, preset_id=None):
assert "hf-secret" not in (paths[0] / "preset.dstack.yml").read_text()
assert "Files remain at" in capsys.readouterr().out
- def test_debug_finalization_does_not_mask_creation_error(self, tmp_path, monkeypatch):
+ def test_finalization_does_not_mask_creation_error(self, tmp_path, monkeypatch):
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
@@ -252,7 +252,6 @@ def fail_finish(self, preset_id=None):
base="Qwen/Qwen3.5-27B",
),
store=PresetStore(tmp_path / "presets"),
- debug=True,
)
@pytest.mark.asyncio
@@ -351,13 +350,12 @@ async def run_agent(**kwargs):
async def test_saves_preset_and_cleans_up_runs(
self, creation_context, monkeypatch, keep_service, stopped_names, tmp_path
):
- session_path = tmp_path / "debug-running"
+ session_path = tmp_path / "session-running"
session_path.mkdir()
(session_path / "agent.log").touch()
(session_path / "trace.jsonl").touch()
session = PresetSession(
path=session_path,
- debug=True,
preset_id="ab12cd34",
)
session.write_state(get_session_state())
@@ -517,7 +515,7 @@ async def test_installs_records_pins_manifest_and_extends_the_prompt(
)
session_path = tmp_path / "fresh"
session_path.mkdir()
- session = PresetSession(path=session_path, debug=False, preset_id="ab12cd34")
+ session = PresetSession(path=session_path, preset_id="ab12cd34")
session.write_state(get_session_state(previous=["8d3b01aa"]))
seen = {}
@@ -550,7 +548,7 @@ async def run_agent(**kwargs):
assert "## Previous Sessions" in seen["prompt"]
assert "8d3b01aa" in seen["prompt"]
assert session.read_state().previous == ["8d3b01aa"]
- # constraints.json is a session record even without --debug.
+ # constraints.json is a persistent session record.
assert (session_path / "constraints.json").is_file()
@@ -598,15 +596,13 @@ async def no_sleep(_):
assert runs.stopped_names == ["qwen-build-1"]
-def _agent_session(tmp_path, *, debug: bool = False) -> PresetSession:
+def _agent_session(tmp_path) -> PresetSession:
path = tmp_path / "agent-running"
path.mkdir()
(path / "agent.log").touch()
- if debug:
- (path / "trace.jsonl").touch()
+ (path / "trace.jsonl").touch()
session = PresetSession(
path=path,
- debug=debug,
preset_id="ab12cd34",
)
session.write_state(get_session_state())
@@ -836,7 +832,7 @@ def test_copies_report_redacted(self, tmp_path):
workspace.final_report_path.write_text(
'{"success": true, "note": "token dstack-secret"}', encoding="utf-8"
)
- session = _agent_session(tmp_path, debug=True)
+ session = _agent_session(tmp_path)
_save_final_report_copy(
workspace=workspace,
@@ -851,7 +847,7 @@ def test_copies_report_redacted(self, tmp_path):
def test_missing_report_is_no_op(self, tmp_path):
workspace = PresetAgentWorkspace(path=tmp_path / "w", dstack_home=tmp_path / "h")
workspace.path.mkdir()
- session = _agent_session(tmp_path, debug=True)
+ session = _agent_session(tmp_path)
_save_final_report_copy(
workspace=workspace,
@@ -894,7 +890,7 @@ def test_suspend_scrubs_workspace_token(self, tmp_path, capsys):
session_dir = tmp_path / "ab12cd34"
session_dir.mkdir()
(session_dir / "agent.log").touch()
- session = PresetSession(path=session_dir, debug=False, preset_id="ab12cd34")
+ session = PresetSession(path=session_dir, preset_id="ab12cd34")
session.write_state(get_session_state())
workspace_root = tmp_path / "workspace"
config_dir = workspace_root / "h" / ".dstack"
@@ -921,7 +917,7 @@ async def test_resume_uses_saved_claude_session(self, creation_context, monkeypa
session_dir = tmp_path / "sessions" / "fe98dc76"
session_dir.mkdir(parents=True)
(session_dir / "agent.log").touch()
- session = PresetSession(path=session_dir, debug=False, preset_id="fe98dc76")
+ session = PresetSession(path=session_dir, preset_id="fe98dc76")
session.write_state(get_session_state(id="fe98dc76"))
workspace, workspace_record = create_agent_workspace(session)
workspace.constraints_path.write_text(
@@ -970,7 +966,7 @@ async def test_pins_user_prompt_on_create(self, creation_context, monkeypatch, t
session_dir = tmp_path / "ab34ef12"
session_dir.mkdir()
(session_dir / "agent.log").touch()
- session = PresetSession(path=session_dir, debug=False, preset_id="ab34ef12")
+ session = PresetSession(path=session_dir, preset_id="ab34ef12")
session.write_state(get_session_state(id="ab34ef12"))
captured = {}
@@ -1008,7 +1004,7 @@ async def test_resume_keeps_the_pinned_user_prompt(
session_dir = tmp_path / "ab34ef12"
session_dir.mkdir()
(session_dir / "agent.log").touch()
- session = PresetSession(path=session_dir, debug=False, preset_id="ab34ef12")
+ session = PresetSession(path=session_dir, preset_id="ab34ef12")
session.write_state(get_session_state(id="ab34ef12"))
workspace, workspace_record = create_agent_workspace(session)
workspace.constraints_path.write_text(
@@ -1075,7 +1071,7 @@ def _session(self, tmp_path, preset_id: str, status: str, log: str) -> PresetSes
session_dir = tmp_path / preset_id
session_dir.mkdir()
(session_dir / "agent.log").write_text(log)
- session = PresetSession(path=session_dir, debug=False, preset_id=preset_id)
+ session = PresetSession(path=session_dir, preset_id=preset_id)
session.write_state(get_session_state(id=preset_id, status=status))
return session
@@ -1126,7 +1122,7 @@ def _detached_session(self, tmp_path, configuration_yaml: str) -> PresetSession:
session_dir.mkdir()
(session_dir / "agent.log").touch()
(session_dir / "preset.dstack.yml").write_text(configuration_yaml)
- session = PresetSession(path=session_dir, debug=False, preset_id="ab12cd34")
+ session = PresetSession(path=session_dir, preset_id="ab12cd34")
workspace, workspace_record = create_agent_workspace(session)
workspace.constraints_path.write_text('{"run_name_prefix": "qwen-build"}')
session.write_state(
@@ -1336,7 +1332,7 @@ def _session(self, tmp_path) -> PresetSession:
(session_dir / "runs.jsonl").write_text(
'{"name":"qwen-build-1","id":"a"}\n{"name":"qwen-build-2","id":"b"}\n'
)
- return PresetSession(path=session_dir, debug=False, preset_id="ab12cd34")
+ return PresetSession(path=session_dir, preset_id="ab12cd34")
def _api(self, statuses: dict, stopped: list) -> SimpleNamespace:
def get(project, name):
@@ -1484,7 +1480,7 @@ def boom(**kwargs):
class TestSessionClaim:
def _session(self, tmp_path):
(tmp_path / "sess").mkdir()
- return PresetSession(path=tmp_path / "sess", debug=False, preset_id="sess")
+ return PresetSession(path=tmp_path / "sess", preset_id="sess")
def test_claim_is_exclusive_and_releasable(self, tmp_path):
session = self._session(tmp_path)
@@ -1533,7 +1529,7 @@ def test_dead_pids_are_not_alive(self):
class TestBeginRun:
def test_records_the_run_whole_and_keeps_claude_state(self, tmp_path):
(tmp_path / "s").mkdir()
- session = PresetSession(path=tmp_path / "s", debug=False, preset_id="s")
+ session = PresetSession(path=tmp_path / "s", preset_id="s")
workspace = PresetSessionWorkspace(path=str(tmp_path / "w"), alias=str(tmp_path / "w"))
session.write_state(
get_session_state(
diff --git a/src/tests/_internal/cli/services/presets/test_workspace.py b/src/tests/_internal/cli/services/presets/test_workspace.py
index 48a7c1aba..ee193fada 100644
--- a/src/tests/_internal/cli/services/presets/test_workspace.py
+++ b/src/tests/_internal/cli/services/presets/test_workspace.py
@@ -27,7 +27,7 @@ def _previous_session(tmp_path, preset_id="8d3b01aa"):
(root / "runs.jsonl").write_text("{}")
(root / "trials" / "not-a-trial").mkdir()
(root / "trials" / "not-a-trial" / "trial.json").write_text("{}")
- return PresetSession(path=root, debug=False, preset_id=preset_id)
+ return PresetSession(path=root, preset_id=preset_id)
def _workspace(tmp_path):
@@ -74,7 +74,7 @@ def test_a_session_without_records_warns(self, tmp_path, capsys):
root = tmp_path / "store" / "empty000"
root.mkdir(parents=True)
(root / "session.json").write_text("{}")
- session = PresetSession(path=root, debug=False, preset_id="empty000")
+ session = PresetSession(path=root, preset_id="empty000")
workspace = _workspace(tmp_path)
install_previous_records(workspace, [session])