mirror of
https://github.com/anthropics/skills.git
synced 2026-08-02 13:05:28 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5754626092 | |||
| 2235be7c60 |
@@ -249,7 +249,7 @@ For placement patterns, architectural guidance, and the silent-invalidator audit
|
||||
|
||||
**Mandatory flow:** Agent (once) → Session (every run). `model`/`system`/`tools` live on the agent, never the session. See `shared/managed-agents-overview.md` for the full reading guide, beta headers, and pitfalls.
|
||||
|
||||
**Beta headers:** `managed-agents-2026-04-01` — the SDK sets this automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores}.*` calls. Skills API uses `skills-2025-10-02` and Files API uses `files-api-2025-04-14`, but you don't need to explicitly pass those in for endpoints other than `/v1/skills` and `/v1/files`.
|
||||
**Beta headers:** `managed-agents-2026-04-01` — the SDK sets this automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores,deployments,deployment_runs}.*` calls. Skills API uses `skills-2025-10-02` and Files API uses `files-api-2025-04-14`, but you don't need to explicitly pass those in for endpoints other than `/v1/skills` and `/v1/files`.
|
||||
|
||||
**Subcommands** — invoke directly with `/claude-api <subcommand>`:
|
||||
|
||||
@@ -257,12 +257,14 @@ For placement patterns, architectural guidance, and the silent-invalidator audit
|
||||
|---|---|
|
||||
| `managed-agents-onboard` | Walk the user through setting up a Managed Agent from scratch. **Read `shared/managed-agents-onboarding.md` immediately** and follow its interview script: mental model → know-or-explore branch → template config → session setup → **pre-flight viability check** → emit code. The viability check (reconcile the stated job against configured tools/credentials/data) catches under-resourced setups — missing a tool, credential, or data access — before the agent burns budget. Do not summarize — run the interview. |
|
||||
|
||||
**Reading guide:** Start with `shared/managed-agents-overview.md`, then the topical `shared/managed-agents-*.md` files (core, environments, tools, events, outcomes, multiagent, webhooks, memory, client-patterns, onboarding, api-reference). For Python, TypeScript, Go, Ruby, PHP, and Java, read `{lang}/managed-agents/README.md` for code examples. For cURL, read `curl/managed-agents.md`. **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI (`ant`) is one convenient way to create agents and environments from version-controlled YAML — see `shared/anthropic-cli.md`. If a binding you need isn't shown in the language README, WebFetch the relevant entry from `shared/live-sources.md` rather than guess. C# has beta Managed Agents support via `client.Beta.Agents` and related namespaces.
|
||||
**Reading guide:** Start with `shared/managed-agents-overview.md`, then the topical `shared/managed-agents-*.md` files (core, environments, tools, events, outcomes, multiagent, webhooks, memory, scheduled-deployments, client-patterns, onboarding, api-reference). For Python, TypeScript, Go, Ruby, PHP, and Java, read `{lang}/managed-agents/README.md` for code examples. For cURL, read `curl/managed-agents.md`. **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI (`ant`) is one convenient way to create agents and environments from version-controlled YAML — see `shared/anthropic-cli.md`. If a binding you need isn't shown in the language README, WebFetch the relevant entry from `shared/live-sources.md` rather than guess. C# has beta Managed Agents support via `client.Beta.Agents` and related namespaces.
|
||||
|
||||
**When the user wants to set up a Managed Agent from scratch** (e.g. "how do I get started", "walk me through creating one", "set up a new agent"): read `shared/managed-agents-onboarding.md` and run its interview — same flow as the `managed-agents-onboard` subcommand.
|
||||
|
||||
**When the user asks "how do I write the client code for X":** reach for `shared/managed-agents-client-patterns.md` — covers lossless stream reconnect, `processed_at` queued/processed gate, interrupt, `tool_confirmation` round-trip, the correct idle/terminated break gate, post-idle status race, stream-first ordering, file-mount gotchas, keeping credentials host-side via custom tools, etc.
|
||||
|
||||
**When the user wants the agent to run on a schedule** (cron, "every night", "weekly report"): read `shared/managed-agents-scheduled-deployments.md` — deployments fire sessions autonomously on a cron cadence, with run records, retries, and auto-pause.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ All endpoints require `x-api-key` and `anthropic-version: 2023-06-01` headers. M
|
||||
anthropic-beta: managed-agents-2026-04-01
|
||||
```
|
||||
|
||||
The SDK adds this header automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores}.*` calls. Skills endpoints use `skills-2025-10-02`; Files endpoints use `files-api-2025-04-14`.
|
||||
The SDK adds this header automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores,deployments,deployment_runs}.*` calls. Skills endpoints use `skills-2025-10-02`; Files endpoints use `files-api-2025-04-14`.
|
||||
|
||||
---
|
||||
|
||||
@@ -26,6 +26,8 @@ All resources are under the `beta` namespace. Python and TypeScript share identi
|
||||
| Session Events | `sessions.events.list` / `send` / `stream` | `Sessions.Events.List` / `Send` / `StreamEvents` |
|
||||
| Session Threads | `sessions.threads.list` / `retrieve` / `archive`; `sessions.threads.events.list` / `stream` | `Sessions.Threads.List` / `Get` / `Archive`; `Sessions.Threads.Events.List` / `StreamEvents` |
|
||||
| Session Resources | `sessions.resources.add` / `retrieve` / `update` / `list` / `delete` | `Sessions.Resources.Add` / `Get` / `Update` / `List` / `Delete` |
|
||||
| Deployments | `deployments.create` / `pause` / `unpause` / `archive` / `run` | Not yet documented — WebFetch the SDK repo (`shared/live-sources.md`) |
|
||||
| Deployment Runs | `deployment_runs.list` (TS: `deploymentRuns.list`) | Not yet documented — WebFetch the SDK repo (`shared/live-sources.md`) |
|
||||
| Vaults | `vaults.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `Vaults.New` / `Get` / `Update` / `List` / `Delete` / `Archive` |
|
||||
| Credentials | `vaults.credentials.create` / `retrieve` / `update` / `list` / `delete` / `archive` / `mcp_oauth_validate` | `Vaults.Credentials.New` / `Get` / `Update` / `List` / `Delete` / `Archive` / `McpOauthValidate` |
|
||||
| Memory Stores | `memory_stores.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `MemoryStores.New` / `Get` / `Update` / `List` / `Delete` / `Archive` |
|
||||
@@ -113,9 +115,29 @@ Per-subagent event streams in multiagent sessions. See `shared/managed-agents-mu
|
||||
|
||||
For `type: "self_hosted"`, `config` is the bare `{"type": "self_hosted"}` — `networking` and `packages` do not apply.
|
||||
|
||||
## Deployments
|
||||
|
||||
Scheduled deployments (`depl_` IDs) run an agent on a recurring cron schedule — each firing creates a session. See `shared/managed-agents-scheduled-deployments.md` for the conceptual guide (cron/DST semantics, failure behavior, lifecycle).
|
||||
|
||||
| Method | Path | Operation | Description |
|
||||
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||
| `POST` | `/v1/deployments` | CreateDeployment | Create a scheduled deployment |
|
||||
| `POST` | `/v1/deployments/{deployment_id}/pause` | PauseDeployment | Suppress scheduled triggers (reversible; manual runs still allowed) |
|
||||
| `POST` | `/v1/deployments/{deployment_id}/unpause` | UnpauseDeployment | Resume from the next occurrence (no backfill) |
|
||||
| `POST` | `/v1/deployments/{deployment_id}/archive` | ArchiveDeployment | **Terminal** — schedule stops, deployment becomes immutable |
|
||||
| `POST` | `/v1/deployments/{deployment_id}/run` | RunDeployment | Trigger a manual run immediately (`trigger_context.type: "manual"`); works while paused |
|
||||
|
||||
## Deployment Runs
|
||||
|
||||
Each trigger attempt (scheduled or manual) writes a `deployment_run` record (`drun_` IDs) carrying either the created `session_id` or an `error.type` (`environment_archived`, `agent_archived`, `vault_not_found`, `session_rate_limited`, `service_unavailable`).
|
||||
|
||||
| Method | Path | Operation | Description |
|
||||
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||
| `GET` | `/v1/deployment_runs?deployment_id=...` | ListDeploymentRuns | List runs for a deployment (paginated; filter failures with `has_error=true`) |
|
||||
|
||||
## Vaults
|
||||
|
||||
Vaults store MCP credentials that Anthropic manages on your behalf — OAuth credentials with auto-refresh, or static bearer tokens. Attach to sessions via `vault_ids`. See `managed-agents-tools.md` §Vaults for the conceptual guide and credential shapes.
|
||||
Vaults store credentials that Anthropic manages on your behalf — MCP credentials (OAuth with auto-refresh, or static bearer tokens) and `environment_variable` credentials substituted into outbound requests at egress. Attach to sessions via `vault_ids`. See `managed-agents-tools.md` §Vaults for the conceptual guide and credential shapes.
|
||||
|
||||
| Method | Path | Operation | Description |
|
||||
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||
@@ -258,7 +280,7 @@ Immutable per-mutation snapshots (`memver_...`) — the audit and rollback surfa
|
||||
"checkout": { "type": "branch", "name": "main" }
|
||||
}
|
||||
],
|
||||
"vault_ids": ["vlt_abc123 (optional — MCP credentials with auto-refresh)"],
|
||||
"vault_ids": ["vlt_abc123 (optional — vault credentials: MCP auth + environment variables)"],
|
||||
"metadata": {
|
||||
"key": "value"
|
||||
}
|
||||
@@ -286,6 +308,26 @@ Immutable per-mutation snapshots (`memver_...`) — the audit and rollback surfa
|
||||
}
|
||||
```
|
||||
|
||||
### CreateDeployment Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Weekly compliance scan",
|
||||
"agent": "agent_abc123 (required — same shapes as CreateSession)",
|
||||
"environment_id": "env_abc123 (required)",
|
||||
"initial_events": [
|
||||
{ "type": "user.message", "content": [{ "type": "text", "text": "Run the weekly compliance scan." }] }
|
||||
],
|
||||
"schedule": {
|
||||
"type": "cron",
|
||||
"expression": "0 20 * * 5",
|
||||
"timezone": "America/New_York"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Optional session config (`resources`, `vault_ids`, etc.) is supported the same way as on CreateSession. Response includes `status`, `paused_reason`, and `schedule.upcoming_runs_at` (next fire times). See `shared/managed-agents-scheduled-deployments.md`.
|
||||
|
||||
### SendEvents Request Body
|
||||
|
||||
```json
|
||||
@@ -304,6 +346,8 @@ Immutable per-mutation snapshots (`memver_...`) — the audit and rollback surfa
|
||||
}
|
||||
```
|
||||
|
||||
> `system.message` events (update the system prompt between turns) use the same envelope with `type: "system.message"` — Claude Opus 4.8 only; see `shared/managed-agents-events.md` § Updating the system prompt mid-session.
|
||||
|
||||
### Define Outcome Event
|
||||
|
||||
```json
|
||||
|
||||
@@ -183,7 +183,9 @@ Delete the original via `files.delete(uploaded.id)`; the session-scoped copy is
|
||||
|
||||
## 9. Secrets for non-MCP APIs and CLIs — keep them host-side via custom tools
|
||||
|
||||
**Problem:** you want the agent to call a third-party API or run a CLI that needs a secret (API key, token, service-account credential), but there is currently no way to set environment variables inside the session container, and vaults currently hold MCP credentials only — they are not exposed to the container's shell. So `curl`, installed CLIs, or SDK clients running via the `bash` tool have no first-class place to read a secret from.
|
||||
**Problem:** you want the agent to call a third-party API or run a CLI that needs a secret (API key, token, service-account credential), but you can't or don't want to hand the secret to a vault.
|
||||
|
||||
**First check:** for cloud environments, the first-class answer is now a vault `environment_variable` credential — the agent's shell sees an opaque placeholder and the real secret is substituted at egress. See `shared/managed-agents-tools.md` → Vaults. Use this pattern instead when that doesn't fit: **self-hosted sandboxes** (env-var credentials not yet supported there), clients that reject the placeholder via local format validation, secrets that must never leave your infrastructure, or calls that need host-side binaries.
|
||||
|
||||
**Solution:** move the authenticated call to your side. Declare a custom tool on the agent; when the agent emits `agent.custom_tool_use`, your orchestrator (the process reading the SSE stream) executes the call with its own credentials and responds with `user.custom_tool_result`. The container never sees the key.
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ const session = await client.beta.sessions.create(
|
||||
| `environment_id`| string | **Yes** | Environment ID |
|
||||
| `title` | string | No | Human-readable name (appears in logs/dashboards) |
|
||||
| `resources` | array | No | Files, GitHub repos, or memory stores, attached to the container at startup. Memory stores are session-create-only (not addable via `resources.add()`). |
|
||||
| `vault_ids` | array | No | Vault IDs (`vlt_*`) — MCP credentials with auto-refresh. See `shared/managed-agents-tools.md` → Vaults. |
|
||||
| `vault_ids` | array | No | Vault IDs (`vlt_*`) — MCP credentials with auto-refresh + `environment_variable` secrets substituted at egress. See `shared/managed-agents-tools.md` → Vaults. |
|
||||
| `metadata` | object | No | User-provided key-value pairs |
|
||||
|
||||
**Agent configuration fields** (passed to `agents.create()`, not `sessions.create()`):
|
||||
|
||||
@@ -13,6 +13,31 @@ Send events to a session via `POST /v1/sessions/{id}/events`.
|
||||
| `user.tool_confirmation` | Approve/deny a tool call (when `always_ask` policy) |
|
||||
| `user.custom_tool_result` | Provide result for a custom tool call |
|
||||
| `user.define_outcome` | Start a rubric-graded iterate loop — see `shared/managed-agents-outcomes.md` |
|
||||
| `system.message` | Update the agent's system prompt between turns — **Claude Opus 4.8 only**; see § Updating the system prompt mid-session |
|
||||
|
||||
#### Updating the system prompt mid-session (`system.message`)
|
||||
|
||||
Unlike the `system` field on the agent definition (fixed at session creation), a `system.message` event changes the system prompt **as the session progresses** — a different persona, revised constraints, or runtime-fetched context that should shape behavior going forward:
|
||||
|
||||
```python
|
||||
client.beta.sessions.events.send(
|
||||
session.id,
|
||||
events=[
|
||||
{
|
||||
"type": "system.message",
|
||||
"content": [
|
||||
{"type": "text", "text": "The user's current timezone is America/New_York."},
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
Constraints:
|
||||
|
||||
- **Claude Opus 4.8 only.** If any model configured on the agent does not support mid-conversation system injection, the event is rejected with a `model_does_not_support_mid_conversation_system` validation error.
|
||||
- **Cannot be sent while the session is idle with `stop_reason: requires_action`** (blocked on `user.custom_tool_result` / `user.tool_confirmation`).
|
||||
- `content` accepts 1–1000 text items.
|
||||
|
||||
### Receiving Events
|
||||
|
||||
|
||||
@@ -83,10 +83,10 @@ Emit as `resources: [{type: "file", file_id, mount_path}]`. Max 999 file resourc
|
||||
|
||||
Per-run. Points at the agent + environment, attaches credentials, kicks off.
|
||||
|
||||
**Vault credentials** (if the agent declared MCP servers):
|
||||
**Vault credentials** (if the agent declared MCP servers, or the job needs an API key for a CLI/SDK/direct API call):
|
||||
- [ ] Existing vault, or create one? (`client.beta.vaults.create()` + `vaults.credentials.create()`)
|
||||
|
||||
Credentials are write-only, matched to MCP servers by URL, auto-refreshed. See `shared/managed-agents-tools.md` → Vaults.
|
||||
Credentials are write-only. MCP credentials are matched to MCP servers by URL and auto-refreshed; `environment_variable` credentials are substituted into outbound requests at egress (the sandbox sees only a placeholder). See `shared/managed-agents-tools.md` → Vaults.
|
||||
|
||||
**Kickoff — pick one:**
|
||||
- [ ] **Conversational:** a first `user.message` to the agent.
|
||||
|
||||
@@ -25,11 +25,11 @@ Managed Agents is in beta. The SDK sets required beta headers automatically:
|
||||
|
||||
| Beta Header | What it enables |
|
||||
| ------------------------------ | ---------------------------------------------------- |
|
||||
| `managed-agents-2026-04-01` | Agents, Environments, Sessions, Events, Session Resources, Session Threads, Outcomes, Multiagent, Vaults, Credentials, Memory Stores |
|
||||
| `managed-agents-2026-04-01` | Agents, Environments, Sessions, Events, Session Resources, Session Threads, Outcomes, Multiagent, Vaults, Credentials, Memory Stores, Deployments |
|
||||
| `skills-2025-10-02` | Skills API (for managing custom skill definitions) |
|
||||
| `files-api-2025-04-14` | Files API for file uploads |
|
||||
|
||||
**Which beta header goes where:** The SDK sets `managed-agents-2026-04-01` automatically on `client.beta.{agents,environments,sessions,vaults,memory_stores}.*` calls, and `files-api-2025-04-14` / `skills-2025-10-02` automatically on `client.beta.files.*` / `client.beta.skills.*` calls. You do NOT need to add the Skills or Files beta header when calling Managed Agents endpoints. **Exception — session-scoped file listing:** `client.beta.files.list({scope_id: session.id})` is a Files endpoint that takes a Managed Agents parameter, so it needs **both** headers. Pass `betas: ["managed-agents-2026-04-01"]` explicitly on that call (the SDK adds the Files header; you add the Managed Agents one). See `shared/managed-agents-environments.md` → Session outputs.
|
||||
**Which beta header goes where:** The SDK sets `managed-agents-2026-04-01` automatically on `client.beta.{agents,environments,sessions,vaults,memory_stores,deployments,deployment_runs}.*` calls, and `files-api-2025-04-14` / `skills-2025-10-02` automatically on `client.beta.files.*` / `client.beta.skills.*` calls. You do NOT need to add the Skills or Files beta header when calling Managed Agents endpoints. **Exception — session-scoped file listing:** `client.beta.files.list({scope_id: session.id})` is a Files endpoint that takes a Managed Agents parameter, so it needs **both** headers. Pass `betas: ["managed-agents-2026-04-01"]` explicitly on that call (the SDK adds the Files header; you add the Managed Agents one). See `shared/managed-agents-environments.md` → Session outputs.
|
||||
|
||||
|
||||
## Reading Guide
|
||||
@@ -53,14 +53,15 @@ Managed Agents is in beta. The SDK sets required beta headers automatically:
|
||||
| Upload files / attach repos | `shared/managed-agents-environments.md` (Resources) |
|
||||
| Give agents persistent memory across sessions | `shared/managed-agents-memory.md` — memory stores, `memory_store` session resource, preconditions, versions/redact |
|
||||
| Define agents/environments as version-controlled YAML; drive the API from the shell | `shared/anthropic-cli.md` — `ant beta:agents create < agent.yaml`, `--transform`, `@file` inlining |
|
||||
| Store MCP credentials | `shared/managed-agents-tools.md` (Vaults section) |
|
||||
| Call a non-MCP API / CLI that needs a secret | `shared/managed-agents-client-patterns.md` Pattern 9 — no container env vars; vaults are MCP-only; keep the secret host-side via a custom tool |
|
||||
| Store credentials (MCP auth, API keys for CLIs/SDKs) | `shared/managed-agents-tools.md` (Vaults section) — `mcp_oauth` / `static_bearer` / `environment_variable` |
|
||||
| Call a non-MCP API / CLI that needs a secret | `shared/managed-agents-tools.md` (Vaults section) — `environment_variable` credential, substituted at egress. If that doesn't fit (e.g. self-hosted sandboxes), `shared/managed-agents-client-patterns.md` Pattern 9 keeps the secret host-side via a custom tool |
|
||||
| Run an agent on a recurring cron schedule | `shared/managed-agents-scheduled-deployments.md` — deployments, deployment runs, pause/auto-pause |
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Agent FIRST, then session — NO EXCEPTIONS** — the session's `agent` field accepts **only** a string ID or `{type: "agent", id, version}`. `model`, `system`, `tools`, `mcp_servers`, `skills` are **top-level fields on `POST /v1/agents`**, never on `sessions.create()`. If the user hasn't created an agent, that is step zero of every example.
|
||||
- **Agent ONCE, not every run** — `agents.create()` is a setup step. Store the returned `agent_id` and reuse it; don't call `agents.create()` at the top of your hot path. If the agent's config needs to change, `POST /v1/agents/{id}` — each update creates a new version, and sessions can pin to a specific version for reproducibility.
|
||||
- **MCP auth goes through vaults** — the agent's `mcp_servers` array declares `{type, name, url}` only (no auth). Credentials live in vaults (`client.beta.vaults.credentials.create`) and attach to sessions via `vault_ids`. Anthropic auto-refreshes OAuth tokens using the stored refresh token.
|
||||
- **MCP auth goes through vaults** — the agent's `mcp_servers` array declares `{type, name, url}` only (no auth). Credentials live in vaults (`client.beta.vaults.credentials.create`) and attach to sessions via `vault_ids`. Anthropic auto-refreshes OAuth tokens using the stored refresh token. Vaults also hold `environment_variable` credentials for non-MCP services (CLIs, SDKs, direct API calls) — substituted at egress, never visible in the sandbox.
|
||||
- **Reconcile resources before the first run** — a session with a clear ask but a missing tool, credential, data mount, or context will discover the gap mid-run, then flail and give up. Before creating the session, check that every action in the task maps to a configured tool/MCP server, every MCP server has a vault credential, and every referenced file/host is mounted/reachable. When helping a user set one up, run the reconciliation in `shared/managed-agents-onboarding.md` → §3 Pre-flight viability check.
|
||||
- **Stream to get events** — `GET /v1/sessions/{id}/events/stream` is the primary way to receive agent output in real-time.
|
||||
- **SSE stream has no replay — reconnect with consolidation** — if the stream drops while a `agent.tool_use`, `agent.mcp_tool_use`, or `agent.custom_tool_use` is pending resolution (`user.tool_confirmation` for the first two, `user.custom_tool_result` for the last one), the session deadlocks (client disconnects → session idles → reconnect happens → no client resolution happens). On every (re)connect: open stream with `GET /v1/sessions/{id}/events/stream` , fetch `GET /v1/sessions/{id}/events`, dedupe by event ID, then proceed. See `shared/managed-agents-events.md` → Reconnecting after a dropped stream.
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
# Managed Agents — Scheduled Deployments
|
||||
|
||||
A **scheduled deployment** runs an agent on a recurring cron schedule — each firing creates a session autonomously. Use it for predictable-cadence work: nightly triage, weekly compliance scans, hourly monitors.
|
||||
|
||||
Requires the `managed-agents-2026-04-01` beta header (the SDK sets it automatically for `client.beta.deployments.*` / `client.beta.deployment_runs.*` calls).
|
||||
|
||||
## Create a deployment
|
||||
|
||||
A deployment bundles everything a session needs (agent, environment, optional files / GitHub / memory stores / vaults) plus a `schedule` and the `initial_events` that kick off each run:
|
||||
|
||||
- `agent` and `environment_id` are required — same shapes as `sessions.create` (see `shared/managed-agents-core.md`).
|
||||
- `initial_events` must contain the starting `user.message`.
|
||||
- `schedule` takes a cron `expression` and an IANA `timezone`. Minute-level granularity is the maximum.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://api.anthropic.com/v1/deployments \
|
||||
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-H "anthropic-beta: managed-agents-2026-04-01" \
|
||||
-H "content-type: application/json" \
|
||||
-d @- <<EOF
|
||||
{
|
||||
"name": "Weekly compliance scan",
|
||||
"agent": "$AGENT_ID",
|
||||
"environment_id": "$ENVIRONMENT_ID",
|
||||
"initial_events": [
|
||||
{"type": "user.message", "content": [{"type": "text", "text": "Run the weekly compliance scan."}]}
|
||||
],
|
||||
"schedule": {
|
||||
"type": "cron",
|
||||
"expression": "0 20 * * 5",
|
||||
"timezone": "America/New_York"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
```python
|
||||
deployment = client.beta.deployments.create(
|
||||
name="Weekly compliance scan",
|
||||
agent=agent.id,
|
||||
environment_id=environment.id,
|
||||
initial_events=[
|
||||
{
|
||||
"type": "user.message",
|
||||
"content": [{"type": "text", "text": "Run the weekly compliance scan."}],
|
||||
},
|
||||
],
|
||||
schedule={
|
||||
"type": "cron",
|
||||
"expression": "0 20 * * 5",
|
||||
"timezone": "America/New_York",
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
The response is a deployment object (`depl_` ID prefix). Check `schedule.upcoming_runs_at` — the next fire times — to confirm the schedule parses the way you intended:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "depl_01xyz",
|
||||
"status": "active",
|
||||
"paused_reason": null,
|
||||
"schedule": {
|
||||
"type": "cron",
|
||||
"expression": "0 20 * * 5",
|
||||
"timezone": "America/New_York",
|
||||
"last_run_at": null,
|
||||
"upcoming_runs_at": ["2026-05-09T00:00:00Z", "2026-05-16T00:00:00Z", "2026-05-23T00:00:00Z"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Deployments may apply up to **10 seconds of jitter** to distribute load. Maximum **1000 scheduled deployments per organization** (contact Anthropic support for more).
|
||||
|
||||
### Cron and timezone semantics
|
||||
|
||||
- **Expression:** standard POSIX cron (`minute hour day-of-month month day-of-week`).
|
||||
- **Timezone:** IANA identifier (e.g. `"America/Los_Angeles"`).
|
||||
- **DST:** literal wall-clock matching — `"0 20 * * *"` in `America/New_York` fires at 8:00 PM local regardless of EST/EDT.
|
||||
|
||||
> ⚠️ **DST edge:** wall-clock times that don't exist on a spring-forward day (e.g. 2AM) are **skipped**; times that occur twice on a fall-back day **fire twice**. Schedule outside the 1–3AM local window, or use UTC, when missed or duplicate executions are unacceptable.
|
||||
|
||||
## Deployment runs
|
||||
|
||||
Every trigger attempt — successful or not — writes a **deployment run** record (`drun_` prefix), so you can audit failures independent of the session lifecycle. A successful run carries the created `session_id`; follow that session via the event stream (`shared/managed-agents-events.md`) or webhooks (`shared/managed-agents-webhooks.md`) as usual. A failed run carries an `error` whose `type` explains why session creation was rejected.
|
||||
|
||||
```python
|
||||
# All runs for a deployment
|
||||
for run in client.beta.deployment_runs.list(deployment_id=deployment.id):
|
||||
print(run.created_at, run.session_id or run.error.type)
|
||||
|
||||
# Failures only
|
||||
for run in client.beta.deployment_runs.list(deployment_id=deployment.id, has_error=True):
|
||||
print(run.created_at, run.error.type, run.error.message)
|
||||
```
|
||||
|
||||
```typescript
|
||||
for await (const run of client.beta.deploymentRuns.list({
|
||||
deployment_id: deployment.id,
|
||||
has_error: true,
|
||||
})) {
|
||||
console.log(run.created_at, run.error?.type, run.error?.message);
|
||||
}
|
||||
```
|
||||
|
||||
Raw HTTP: `GET /v1/deployment_runs?deployment_id=...&has_error=true`.
|
||||
|
||||
A failed run looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "deployment_run",
|
||||
"id": "drun_01abc124",
|
||||
"deployment_id": "depl_01xyz",
|
||||
"trigger_context": { "type": "schedule", "scheduled_at": "2026-05-09T00:00:00Z" },
|
||||
"session_id": null,
|
||||
"error": { "type": "environment_archived", "message": "environment `env_01abc` is archived" },
|
||||
"agent": { "type": "agent", "id": "agent_01ghi789", "version": 3 },
|
||||
"created_at": "2026-05-09T00:00:01Z"
|
||||
}
|
||||
```
|
||||
|
||||
Error types include `environment_archived`, `agent_archived`, `vault_not_found`, `session_rate_limited`, and `service_unavailable`.
|
||||
|
||||
## Lifecycle: pause / unpause / archive
|
||||
|
||||
| Operation | SDK | Effect |
|
||||
|---|---|---|
|
||||
| Pause | `client.beta.deployments.pause(id)` | Suppresses scheduled triggers go-forward. Sessions already running continue. **Manual runs are still permitted while paused.** Sets `paused_reason: {"type": "manual"}`. |
|
||||
| Unpause | `client.beta.deployments.unpause(id)` | Resumes from the next scheduled occurrence. **Missed triggers are not backfilled.** Clears `paused_reason`. |
|
||||
| Archive | `client.beta.deployments.archive(id)` | **Terminal** — the schedule stops and the deployment can no longer be modified. Use pause for anything reversible. |
|
||||
|
||||
Raw HTTP: `POST /v1/deployments/{deployment_id}/pause` (likewise `/unpause`, `/archive`).
|
||||
|
||||
### Failure behavior
|
||||
|
||||
- **Rate-limited:** recorded immediately as a `session_rate_limited` run, **no retry** — the schedule simply tries again at the next occurrence. (Rate limits on API calls *inside* a session are handled by the session itself.)
|
||||
- **Other failed runs** (e.g. `environment_archived`, `vault_not_found`, `service_unavailable`): the run records the `error.type` — monitor runs and fix the referenced resource, or pause the deployment.
|
||||
- **Agent archived or deleted:** the deployment is automatically **archived** (terminal) and no further sessions are created.
|
||||
|
||||
## Manual runs
|
||||
|
||||
`POST /v1/deployments/{deployment_id}/run` (SDK: `client.beta.deployments.run(id)`) creates a session immediately and writes a run with `trigger_context.type: "manual"`. Use it to **test a deployment before committing to the schedule** — and remember it works even while the deployment is paused.
|
||||
@@ -156,6 +156,7 @@ These are **control-plane** calls — authenticate with `x-api-key` (not the env
|
||||
| Container lifecycle, hardening, networking | Anthropic | **You** — run non-root, read-only rootfs, drop caps; egress is whatever your VPC/firewall allows |
|
||||
| `file` / `github_repository` resource mounting | Anthropic mounts into the container | **You** — pass pointers via `sessions.create(metadata={...})` and have your orchestrator fetch/clone before dispatch |
|
||||
| `memory_store` resources | Supported | **Not yet supported** |
|
||||
| Vault `environment_variable` credentials | Supported (substituted at Anthropic-managed egress) | **Not yet supported** — egress is yours, so there's nowhere to substitute the secret. Use MCP credentials or a host-side custom tool (`shared/managed-agents-client-patterns.md` Pattern 9) |
|
||||
| Built-in tools | Via `agent_toolset_20260401` | Supplied by your worker (`EnvironmentWorker` default / `beta_agent_toolset(env)` / `ant` CLI fixed set) |
|
||||
| Skills download | Automatic | `EnvironmentWorker` / `AgentToolContext` fetch into `{workdir}/skills/` (needs `client` + `session_id`) |
|
||||
| Claude Platform on AWS | Supported | **Not available** |
|
||||
|
||||
@@ -190,9 +190,14 @@ This keeps secrets out of reusable agent definitions. Each vault credential is t
|
||||
|
||||
> ⚠️ **MCP auth tokens ≠ REST API tokens.** Hosted MCP servers (`mcp.notion.com`, `mcp.linear.app`, etc.) typically require **OAuth bearer tokens**, not the service's native API keys. A Notion `ntn_` integration token authenticates against Notion's REST API but will **not** work as a vault credential for the Notion MCP server. These are different auth systems.
|
||||
|
||||
### Vaults — the MCP credential store
|
||||
### Vaults — the credential store
|
||||
|
||||
**Vaults** store OAuth credentials (access token + refresh token) that Anthropic auto-refreshes on your behalf via standard OAuth 2.0 `refresh_token` grant. This is the only way to authenticate MCP servers in the launch SDK.
|
||||
**Vaults** store credentials that Anthropic manages on your behalf. Two credential categories:
|
||||
|
||||
- **MCP credentials** (`mcp_oauth`, `static_bearer`) — keyed by `mcp_server_url`. When the agent connects to a server at that URL, the token is injected automatically. `mcp_oauth` tokens are auto-refreshed via the standard OAuth 2.0 `refresh_token` grant. This is the only way to authenticate MCP servers.
|
||||
- **Environment variables** (`environment_variable`) — keyed by `secret_name` (the env var name). The sandbox sees only an **opaque placeholder**; the real secret is substituted into the outbound request **at egress**. Use this for any service that authenticates through an environment variable: CLIs (`aws`, `gcloud`, `stripe`), SDKs, or direct `curl` calls from the `bash` tool.
|
||||
|
||||
Secret fields you supply (`token`, `access_token`, `refresh_token`, `client_secret`, `secret_value`) are write-only — never returned in API responses.
|
||||
|
||||
#### Credentials and the sandbox
|
||||
|
||||
@@ -200,11 +205,9 @@ Vaults store credentials; those credentials **never enter the sandbox**. This is
|
||||
|
||||
- **MCP tool calls** are routed through an Anthropic-side proxy that fetches the credential from the vault and adds it to the outbound request.
|
||||
- **Git operations on attached GitHub repositories** (`git pull`, `git push`, GitHub REST calls) are routed through a git proxy that injects the `github_repository` resource's `authorization_token` the same way.
|
||||
- **Environment-variable credentials** appear in the sandbox as an opaque placeholder; the real value replaces the placeholder at egress, on requests to the credential's allowed hosts only.
|
||||
|
||||
**Not yet supported:** running other authenticated CLIs (e.g. `aws`, `gcloud`, `stripe`) directly inside the sandbox. There is currently no way to set container environment variables or expose vault credentials to arbitrary processes. If you need one of these today:
|
||||
|
||||
- **Prefer an MCP server** for that service if one exists — it gets the same vault-backed injection.
|
||||
- **Otherwise, register a custom tool:** the agent emits `agent.custom_tool_use`, your orchestrator (which already holds the credential) executes the call and returns `user.custom_tool_result` over the same authenticated event stream. No public endpoint is exposed; the sandbox never sees the secret. See `shared/managed-agents-client-patterns.md` → Pattern 9.
|
||||
**When vault credentials don't fit** (e.g. self-hosted sandboxes — `environment_variable` is not yet supported there), **register a custom tool:** the agent emits `agent.custom_tool_use`, your orchestrator (which already holds the credential) executes the call and returns `user.custom_tool_result` over the same authenticated event stream. No public endpoint is exposed; the sandbox never sees the secret. See `shared/managed-agents-client-patterns.md` → Pattern 9.
|
||||
|
||||
**Do not put API keys in the system prompt or user messages as a workaround** — they persist in the session's event history.
|
||||
|
||||
@@ -213,11 +216,11 @@ Vaults store credentials; those credentials **never enter the sandbox**. This is
|
||||
**Flow:**
|
||||
|
||||
1. Create a vault (`client.beta.vaults.create(...)`) — one per tenant/user, or one shared, depending on your model
|
||||
2. Add MCP credentials to it (`client.beta.vaults.credentials.create(...)`) — each credential is tied to one MCP server URL
|
||||
2. Add credentials to it (`client.beta.vaults.credentials.create(...)`) — MCP credentials are keyed by MCP server URL; environment-variable credentials by `secret_name`
|
||||
3. Reference the vault on session create via `vault_ids: ["vlt_..."]`
|
||||
4. Anthropic auto-refreshes tokens before they expire; the agent uses the current access token when calling MCP tools
|
||||
4. Anthropic auto-refreshes OAuth tokens before they expire and substitutes secrets at runtime
|
||||
|
||||
**Credential shape**:
|
||||
**MCP OAuth credential shape**:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -249,6 +252,40 @@ Omit `refresh` entirely if you only have an access token with no refresh capabil
|
||||
|
||||
> 💡 **Getting an OAuth token.** How you obtain the initial access and refresh tokens depends on the MCP server — consult its documentation. Once you have them, store them in a vault credential using the shape above; Anthropic auto-refreshes via the `refresh.token_endpoint` from there.
|
||||
|
||||
**Environment-variable credential shape**:
|
||||
|
||||
```json
|
||||
{
|
||||
"display_name": "Twilio API key for sandbox",
|
||||
"auth": {
|
||||
"type": "environment_variable",
|
||||
"secret_name": "TWILIO_API_KEY",
|
||||
"secret_value": "sk-your-secret-here",
|
||||
"networking": {
|
||||
"type": "limited",
|
||||
"allowed_hosts": ["api.twilio.com", "*.twilio.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`networking.allowed_hosts` controls which outbound hosts the secret can be substituted for — `{"type": "limited", "allowed_hosts": [...]}` or `{"type": "unrestricted"}` if you can't enumerate the domains in advance. Limiting is strongly recommended: it prevents the key from ever being sent to unauthorized hosts.
|
||||
|
||||
> ⚠️ **Two networking layers, both required.** `networking.allowed_hosts` on the credential controls which requests *use the secret*, not which requests are *allowed*. The agent must also be able to reach the domain at the **environment level** (`unrestricted`, or the host listed in the environment's `allowed_hosts` — see `shared/managed-agents-environments.md`). A domain missing from either layer means the secret-substituted request fails.
|
||||
|
||||
> ⚠️ **Client-side validation caveat.** Substitution happens at egress, not inside the sandbox — clients that validate the credential *format* locally before making a network request (e.g. a CLI that checks the key starts with `sk-`) will see the opaque placeholder and may fail at startup. If a client rejects the credential before any network call, that's why.
|
||||
|
||||
> 💡 **Scope the key minimally.** The agent can do anything the key allows; a key with broader permissions than the task needs increases the blast radius if the agent behaves unexpectedly.
|
||||
|
||||
**Not supported with self-hosted sandboxes** — `environment_variable` credentials require Anthropic-managed egress. See `shared/managed-agents-self-hosted-sandboxes.md`.
|
||||
|
||||
**Constraints (all credential types):**
|
||||
|
||||
- **Unique key per vault.** `mcp_server_url` (MCP credentials) and `secret_name` (environment-variable credentials) must be unique among active credentials in a vault; duplicates return a 409.
|
||||
- **Keys are immutable.** Secret values and `display_name` can be updated (rotation); to change `mcp_server_url`, `secret_name`, `token_endpoint`, or `client_id`, archive the credential and create a new one. Archiving purges the secret and frees the key for a replacement.
|
||||
- **Maximum 20 credentials per vault.**
|
||||
- Credentials are stored as provided and **not validated until session runtime** — an invalid credential surfaces as an authentication or downstream error during the session, which is emitted but does not block the session from continuing.
|
||||
|
||||
**Scoping:** Vaults are workspace-scoped. Anyone with developer+ role in the API workspace can create, read (metadata only — secrets are write-only), and attach vaults. `vault_ids` can be set at session **create** time but not via session update (the SDK docstring says "Not yet supported; requests setting this field are rejected").
|
||||
|
||||
---
|
||||
|
||||
@@ -1,42 +1,55 @@
|
||||
---
|
||||
name: frontend-design
|
||||
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.
|
||||
description: Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
|
||||
# Frontend Design
|
||||
|
||||
The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
|
||||
Approach this as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. This client has already rejected proposals that felt templated, and is paying for a distinctive point of view: make deliberate, opinionated choices about palette, typography, and layout that are specific to this brief, and take one real aesthetic risk you can justify.
|
||||
|
||||
## Design Thinking
|
||||
## Ground it in the subject
|
||||
|
||||
Before coding, understand the context and commit to a BOLD aesthetic direction:
|
||||
- **Purpose**: What problem does this interface solve? Who uses it?
|
||||
- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
|
||||
- **Constraints**: Technical requirements (framework, performance, accessibility).
|
||||
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
|
||||
If the brief does not pin down what the product or subject is, pin it yourself before designing: name one concrete subject, its audience, and the page's single job, and state your choice. If there's any information in your memory about the human's preferences, context about what they're building, or designs you've made before – use that as a hint. The subject's own world, its materials, instruments, artifacts, and vernacular, is where distinctive choices come from. Build with the brief's real content and subject matter throughout.
|
||||
|
||||
**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.
|
||||
## Design principles
|
||||
|
||||
Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
|
||||
- Production-grade and functional
|
||||
- Visually striking and memorable
|
||||
- Cohesive with a clear aesthetic point-of-view
|
||||
- Meticulously refined in every detail
|
||||
For web designs, the hero is a thesis. Open with the most characteristic thing in the subject's world, in whatever form makes sense for it: a headline, an image, an animation, a live demo, an interactive moment. Be deliberate with your choice: a big number with a small label, supporting stats, and a gradient accent is the template answer, only use if that's truly the best option.
|
||||
|
||||
## Frontend Aesthetics Guidelines
|
||||
Typography carries the personality of the page. Pair the display and body faces deliberately, not the same families you would reach for on any other project, and set a clear type scale with intentional weights, widths, and spacing. Make the type treatment itself a memorable part of the design, not a neutral delivery vehicle for the content.
|
||||
|
||||
Focus on:
|
||||
- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
|
||||
- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
|
||||
- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
|
||||
- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
|
||||
- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.
|
||||
Structure is information. Structural devices, numbering, eyebrows, dividers, labels, should encode something true about the content, not decorate it. Many generic designs use numbered markers (01 / 02 / 03), but that's only appropriate if the content actually is a sequence - like a real process or a typed timeline where order carries information the reader needs. Question if choices like numbered markers actually make sense before incorporating them.
|
||||
|
||||
NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.
|
||||
Leverage motion deliberately. Think about where and if animation can serve the subject: a page-load sequence, a scroll-triggered reveal, hover micro-interactions, ambient atmosphere. An orchestrated moment usually lands harder than scattered effects; choose what the direction calls for. However, sometimes less is more, and extra animation contributes to the feeling that the design is AI-generated.
|
||||
|
||||
Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.
|
||||
Match complexity to the vision. Maximalist directions need elaborate execution; minimal directions need precision in spacing, type, and detail. Elegance is executing the chosen vision well.
|
||||
|
||||
**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.
|
||||
Consider written content carefully. Often a design brief may not contain real content, and it's up to you to come up with copy. Copy can make a design feel as templated as the design itself. See the below section on writing for more guidance.
|
||||
|
||||
Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
|
||||
## Process: brainstorm, explore, plan, critique, build, critique again
|
||||
|
||||
For calibration: AI-generated design right now clusters around three looks: (1) a warm cream background (near #F4F1EA) with a high-contrast serif display and a terracotta accent; (2) a near-black background with a single bright acid-green or vermilion accent; (3) a broadsheet-style layout with hairline rules, zero border-radius, and dense newspaper-like columns. All three are legitimate for some briefs, but they are defaults rather than choices, and they appear regardless of subject. Where the brief pins down a visual direction, follow it exactly — the brief's own words always win, including when it asks for one of these looks. Where it leaves an axis free, don't spend that freedom on one of these defaults. Just like a human designer who's hired, there's often a careful balance between doing what you're good at and taking each project as a chance to experiment and learn.
|
||||
|
||||
Work in two passes. First, brainstorm a short design plan based on the human's design brief: create a compact token system with color, type, layout, and signature. Color: describe the palette as 4–6 named hex values. Type: the typefaces for 2+ roles (a characterful display face that's used with restraint, a complementary body face, and a utility face for captions or data if needed). Layout: a layout concept, using one-sentence prose descriptions and ASCII wireframes to ideate and compare. Signature: the single unique element this page will be remembered by that embodies the brief in an appropriate way.
|
||||
|
||||
Then review that plan against the brief before building: if any part of it reads like the generic default you would produce for any similar page (work through a similar prompt to see if you arrive somewhere similar) rather than a choice made for this specific brief — revise that part, say what you changed and why. Only after you've confirmed the relative uniqueness of your design plan should you start to write the code, following the revised plan exactly and deriving every color and type decision from it.
|
||||
|
||||
When writing the code, be careful of structuring your CSS selector specificities. It's easy to generate CSS classes that cancel each other out (especially with a type-based selector like .section and a element-based selector like .cta). This can happen often with paddings/margins between sections.
|
||||
|
||||
Try to do a lot of this planning and iteration in your thinking, and only show ideas to the user when you have higher confidence it'll delight them.
|
||||
|
||||
## Restraint and self-critique
|
||||
|
||||
Spend your boldness in one place. Let the signature element be the one memorable thing, keep everything around it quiet and disciplined, and cut any decoration that does not serve the brief. Not taking a risk can be a risk itself! Build to a quality floor without announcing it: responsive down to mobile, visible keyboard focus, reduced motion respected. Critique your own work as you build, taking screenshots if your environment supports it – a picture is worth 1000 tokens. Consider Chanel's advice: before leaving the house, take a look in the mirror and remove one accessory. Human creators have memory and always try to do something new, so if you have a space to quickly jot down notes about what you've tried, it can help you in future passes.
|
||||
|
||||
## More on writing in design
|
||||
|
||||
Words appear in a design for one reason: to make it easier to understand, and therefore easier to use. They are design material, not decoration. Bring the same intentionality to copy that you would bring to spacing and color. Before writing anything, ask what the design needs to say, and how it can best be said to help the person navigate the experience.
|
||||
|
||||
Write from the end user's side of the screen. Name things by what people control and recognize, never by how the system is built. A person manages notifications, not webhook config. Describe what something does in plain terms rather than selling it. Being specific is always better than being clever.
|
||||
|
||||
Use active voice as default. A control should say exactly what happens when it's used: "Save changes," not "Submit." An action keeps the same name through the whole flow, so the button that says "Publish" produces a toast that says "Published." The vocabulary of an interface is the signposting for someone navigating the product. Cohesion and consistency are how people learn their way around.
|
||||
|
||||
Treat failure and emptiness as moments for direction, not mood. Explain what went wrong and how to fix it, in the interface's voice rather than a person's. Errors don't apologize, and they are never vague about what happened. An empty screen is an invitation to act.
|
||||
|
||||
Keep the register conversational and tuned: plain verbs, sentence case, no filler, with tone matched to the brand and the audience. Let each element do exactly one job. A label labels, an example demonstrates, and nothing quietly does double duty.
|
||||
|
||||
Reference in New Issue
Block a user