Skip to content

Commit a854371

Browse files
Merge origin/main into fix/test-leaked-skill-syncs
Resolves launch-resolve and routing-pin against workspaces-on-by-default: both sides only added declarations. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174HUYiaceApqHP7EKMNgnb
2 parents 2f2327b + c78b284 commit a854371

57 files changed

Lines changed: 576 additions & 325 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/docs/configure/learn.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ altimate-code learn promote --publish
266266
|---|---|
267267
| `--yes` | Skip the confirmation prompt. Required outside a terminal. |
268268
| `--allow-flagged` | With `--yes`: approve lessons flagged for mentioning skipping or disabling verification. |
269-
| `--publish` | After promoting, export the approved lessons as a skill and publish it to the linked workspace. Needs `ALTIMATE_WORKSPACE=1`. |
269+
| `--publish` | After promoting, export the approved lessons as a skill and publish it to the linked workspace. Not available when `ALTIMATE_DISABLE_WORKSPACE` is set. |
270270
| `--replace` | With `--publish`: update your own same-name published playbook even if it was published from another checkout. |
271271

272272
### pin / unpin

‎docs/docs/configure/skills.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ altimate-code skill install owner/repo --global # install globally
193193
# Remove
194194
altimate-code skill remove my-tool # remove skill + paired tool
195195

196-
# Publish to the linked workspace (pilot, requires ALTIMATE_WORKSPACE=1)
196+
# Publish to the linked workspace
197197
altimate-code skill publish my-tool # upload every file in the skill directory; re-run to update
198198
```
199199

@@ -210,7 +210,7 @@ Open the skill browser by typing `/skills` in the prompt (or `<leader>k`):
210210
| Key | Action |
211211
|-----|--------|
212212
| Enter | Use — inserts `/<skill-name>` into the prompt |
213-
| `ctrl+a` | Actions — show, edit, test, remove, or publish the selected skill to the linked workspace (the publish row appears only with `ALTIMATE_WORKSPACE=1`) |
213+
| `ctrl+a` | Actions — show, edit, test, remove, or publish the selected skill to the linked workspace (the publish row is unavailable when `ALTIMATE_DISABLE_WORKSPACE=1` is set) |
214214
| `ctrl+o` | New — scaffold a new skill + CLI tool (`ctrl+n` moves down the list and `ctrl+e` is line-end in the filter box, as in every dialog) |
215215
| `ctrl+g` | Install a skill from a GitHub repo, URL, or local path (`ctrl+i` is Tab in most terminals, so it cannot be the chord) |
216216
| Tab / Shift+Tab | Move between the **Actions · New · Install** buttons in the footer, then Enter — the same three without a chord |

‎docs/docs/usage/cli.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,20 @@ altimate --agent analyst
3939
| `export` | Export session data |
4040
| `import` | Import session data |
4141
| `session` | Session management |
42-
| `link` | Link this project to an Altimate workspace (pilot, requires `ALTIMATE_WORKSPACE=1`) |
43-
| `workspace` | Show, refresh, sync or unlink this project's workspace from a shell (pilot, requires `ALTIMATE_WORKSPACE=1`) |
42+
| `link` | Link this project to an Altimate workspace |
43+
| `workspace` | Show, refresh, sync or unlink this project's workspace from a shell |
4444
| `trace` | List and view session traces (recordings of agent sessions) |
4545
| `github` | GitHub integration |
4646
| `pr` | Pull request tools |
4747
| `debug` | Troubleshooting tools -- `debug bundle` writes a diagnostic report to send to support; see [Debug Mode](../reference/troubleshooting.md#debug-mode) |
4848
| `upgrade` | Upgrade to latest version |
4949
| `uninstall` | Uninstall altimate |
5050

51-
### Workspaces (pilot)
51+
### Workspaces
5252

53-
Workspace features are off unless `ALTIMATE_WORKSPACE=1` is set. With it:
53+
Workspace features are on by default. When you are signed in to Altimate, altimate-code asks the Altimate service whether the project is linked to a workspace: it sends the project's git `origin` URL (credentials removed), and only if that finds no link, the project's folder path. The answer is kept in memory by the running process and asked again at most every 5 minutes; a failed request is retried on the next turn. Nothing is sent when you are not signed in. Set `ALTIMATE_DISABLE_WORKSPACE=1` to turn all of them off, these lookups included (the value is trimmed and read without regard to case; anything other than `0`, `false` or an empty value counts as set): no post-scan prompt, no `link` or `skill publish`, no `/workspace` menu or sidebar, no skill or memory sync, and no workspace engine. In a project that is already linked, the link is ignored rather than removed: workspace skills already pulled into the project are taken out of service, the agent is not told about the workspace, and the MCP entries the workspace manages can be edited by hand again. Unset it and the link applies again.
54+
55+
With workspaces on:
5456

5557
- `altimate-code link` links the current project to a workspace (or creates one). The sidebar then names the workspace and shows how many memories are not yet synced and when skills last synced.
5658
- From a shell, script or CI job, without the TUI:
@@ -71,7 +73,7 @@ Workspace features are off unless `ALTIMATE_WORKSPACE=1` is set. With it:
7173
| `--agent <name>` | Start with a specific agent |
7274
| `--yolo` | Auto-approve all permission prompts (explicit `deny` rules still enforced) |
7375
| `--dangerously-skip-permissions` | Same as `--yolo` (alias for upstream compatibility); auto-approves prompts that aren't explicitly denied. `run` subcommand only. |
74-
| `--integrations <local>` | Use only local warehouse tools instead of routing them through a bound workspace's engine (pilot). Sets `ALTIMATE_INTEGRATIONS` for the process, so child processes inherit it. |
76+
| `--integrations <local>` | Use only local warehouse tools instead of routing them through a bound workspace's engine. Sets `ALTIMATE_INTEGRATIONS` for the process, so child processes inherit it. |
7577
| `--print-logs` | Print logs to stderr |
7678
| `--log-level <level>` | Set log level: `DEBUG`, `INFO`, `WARN`, `ERROR` |
7779
| `--help`, `-h` | Show help |
@@ -104,7 +106,7 @@ Configuration can be controlled via environment variables:
104106
| `ALTIMATE_CLI_DISABLE_TERMINAL_TITLE` | Don't set terminal title |
105107
| `ALTIMATE_CLI_DISABLE_PRUNE` | Disable database pruning |
106108
| `ALTIMATE_CLI_DISABLE_MODELS_FETCH` | Don't fetch models from models.dev |
107-
| `ALTIMATE_WORKSPACE` | Opt into the workspace pilot (`1`). Off by default; nothing about workspaces is active without it |
109+
| `ALTIMATE_DISABLE_WORKSPACE` | Set to `1` to turn off every workspace feature (linking, skill and memory sync, the workspace engine, and the project lookup each session makes). The value is trimmed and read without regard to case; anything other than `0`, `false` or an empty value counts as set. Workspaces are on by default |
108110
| `ALTIMATE_WORKSPACE_WEB_URL` | Development only: the base URL of a local web app's Workspaces pages, used instead of the tenant's for `link`'s browser hand-off and workspace links. Include the mount path, e.g. `http://acme.localhost:3000/workspaces` |
109111
| `ALTIMATE_INTEGRATIONS` | Set to `local` to keep warehouse tools local rather than routing them through a bound workspace's engine |
110112
| `ALTIMATE_CODE_SERVE` | Set to `1` by `altimate-code serve` itself, whether the IDE extension or you launched it. Marks that process as the extension's host — so it is the one that reads the pin variables — and is stripped from every child the bash and shell tools start |

‎packages/core/src/flag/flag.ts‎

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,19 @@ export const Flag = {
113113
// altimate_change start — documented ALTIMATE_CLI_ name read first (see `env`)
114114
OPENCODE_WORKSPACE_ID: env("OPENCODE_WORKSPACE_ID"),
115115
// altimate_change end
116-
// Unrelated to ALTIMATE_WORKSPACE (SaaS project-binding pilot) — this gates upstream's multi-instance/worktree control plane.
116+
// Unrelated to ALTIMATE_DISABLE_WORKSPACE (SaaS project-binding kill switch) — this gates upstream's multi-instance/worktree control plane.
117117
OPENCODE_EXPERIMENTAL_WORKSPACES: enabledByExperimental("OPENCODE_EXPERIMENTAL_WORKSPACES"),
118118

119-
// altimate_change start — pilot flag for the Workspaces feature (post-scan prompt +
120-
// altimate link subcommand). Read as a getter so tests and the runtime `--` middleware
121-
// can flip it between plugin activation and command execution.
122-
//
123-
// Opt-in only — deliberately does NOT inherit ``OPENCODE_EXPERIMENTAL`` (as
124-
// ``enabledByExperimental`` would). The pilot ships behind its own explicit
125-
// gate so users already opted into other experimental features don't get
126-
// this one turned on for them. (Kilo cycle 6.)
127-
// Unrelated to OPENCODE_EXPERIMENTAL_WORKSPACES (multi-instance control plane) — this gates the SaaS project-binding pilot.
128-
get ALTIMATE_WORKSPACE() {
129-
return truthy("ALTIMATE_WORKSPACE")
119+
// altimate_change start — kill switch for the Workspaces feature (project linking, skill and
120+
// memory sync, the workspace engine). Workspaces are on by default; setting this turns every
121+
// part of the feature off and takes already-synced workspace skills out of service. Read as a getter so tests and the
122+
// runtime `--` middleware can flip it between plugin activation and command execution.
123+
// Unrelated to OPENCODE_EXPERIMENTAL_WORKSPACES (multi-instance control plane).
124+
get ALTIMATE_DISABLE_WORKSPACE() {
125+
// A kill switch fails closed: any value but empty, `0` or `false` turns workspaces off, so `yes`
126+
// or `on` cannot leave them running while the operator believes they are off.
127+
const value = env("ALTIMATE_DISABLE_WORKSPACE")?.trim().toLowerCase()
128+
return value !== undefined && value !== "" && value !== "0" && value !== "false"
130129
},
131130
/**
132131
* Workspace precedence escape hatch, set by `--integrations=local`. When on, the

‎packages/opencode/src/altimate/plugin/onboarding-telemetry.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import * as OnboardingTelemetry from "../telemetry/onboarding"
1515
// altimate_change start — AI-8398 workspaces trigger. Reaches into the same
1616
// EventV2 bridge the server/routes/tui.ts uses to publish TuiEvent.CommandExecute
1717
// so the workspace TuiPlugin (packages/opencode/src/plugin/tui/altimate/workspace.tsx)
18-
// runs its post-scan flow. Feature-flagged via Flag.ALTIMATE_WORKSPACE.
18+
// runs its post-scan flow. Off when Flag.ALTIMATE_DISABLE_WORKSPACE is set.
1919
import { Effect } from "effect"
2020
import { Flag } from "@opencode-ai/core/flag/flag"
2121
import { AltimateApi } from "@/altimate/api/client"
@@ -244,7 +244,7 @@ export async function OnboardingTelemetryPlugin(_input: PluginInput): Promise<Ho
244244
// ARM (don't publish yet) — the dialog fires when the session goes idle,
245245
// not the moment project_scan returns. See armWorkspacePromptOnSessionIdle
246246
// above for why the immediate publish raced the LLM's ongoing streaming.
247-
if (Flag.ALTIMATE_WORKSPACE && (await AltimateApi.isConfigured().catch(() => false))) {
247+
if (!Flag.ALTIMATE_DISABLE_WORKSPACE && (await AltimateApi.isConfigured().catch(() => false))) {
248248
void armWorkspacePromptOnSessionIdle(input.sessionID)
249249
}
250250
// altimate_change end

‎packages/opencode/src/altimate/tools/datamate.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ async function handleAdd(args: { datamate_id?: string; name?: string; scope?: "p
266266
output:
267267
`This project is linked to workspace "${managed.name}", whose integrations are served by the ` +
268268
`workspace's own engine under the '${DATAMATE_KEY}' MCP server. Adding datamate '${args.datamate_id}' ` +
269-
`there is not applied. Unlink the project, or restart with ALTIMATE_WORKSPACE unset, to manage that entry by hand.`,
269+
`there is not applied. Unlink the project, or restart with ALTIMATE_DISABLE_WORKSPACE=1, to manage that entry by hand.`,
270270
}
271271
}
272272
// altimate_change end
@@ -493,7 +493,7 @@ async function handleCreate(args: {
493493
output:
494494
`This project is linked to workspace "${managedKey.name}", whose integrations are served by the ` +
495495
`workspace's own engine under the '${DATAMATE_KEY}' MCP server. Creating datamate '${args.name}' ` +
496-
`here would not connect it. Unlink the project, or restart with ALTIMATE_WORKSPACE unset, first.`,
496+
`here would not connect it. Unlink the project, or restart with ALTIMATE_DISABLE_WORKSPACE=1, first.`,
497497
}
498498
}
499499
}
@@ -672,7 +672,7 @@ async function handleRemove(args: { server_name?: string; scope?: "project" | "g
672672
output:
673673
`This project is linked to workspace "${managedKey.name}", whose integrations are served by the ` +
674674
`workspace's own engine under the '${DATAMATE_KEY}' MCP server. It is not removed. Unlink the project, ` +
675-
`or restart with ALTIMATE_WORKSPACE unset, to manage that entry by hand.`,
675+
`or restart with ALTIMATE_DISABLE_WORKSPACE=1, to manage that entry by hand.`,
676676
}
677677
}
678678
// altimate_change end

‎packages/opencode/src/altimate/tools/mcp-discover.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export const McpDiscoverTool = Tool.define("mcp_discover", {
137137
if (managed) {
138138
lines.push(
139139
`\n'${DATAMATE_KEY}' was not added: this project is linked to workspace "${managed.name}", ` +
140-
`whose engine serves that server. Unlink the project, or restart with ALTIMATE_WORKSPACE unset, to add it by hand.`,
140+
`whose engine serves that server. Unlink the project, or restart with ALTIMATE_DISABLE_WORKSPACE=1, to add it by hand.`,
141141
)
142142
continue
143143
}

‎packages/opencode/src/altimate/workspace/engine-seams.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const syncInternals: {
6565
} = {}
6666

6767
export function isEnabled(): boolean {
68-
return CoreFlag.ALTIMATE_WORKSPACE
68+
return !CoreFlag.ALTIMATE_DISABLE_WORKSPACE
6969
}
7070

7171
/** Headless `run`: no TUI can render a toast, so refusals print one stderr

‎packages/opencode/src/altimate/workspace/launch-resolve.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ export async function resolveWorkspaceForLaunch(
3737
directory: string,
3838
explicitName: string | undefined,
3939
): Promise<void> {
40-
// Gate on the pilot flag so the flag is invisible to non-opted-in users
41-
// even if they discover it via ``--help``. Silent when opted out — no
42-
// error message that would leak the pilot's existence.
43-
if (!Flag.ALTIMATE_WORKSPACE) return
40+
// Silent under the kill switch: `--workspace` is hidden from ``--help`` then,
41+
// and a user who passes it anyway gets no workspace and no error.
42+
if (Flag.ALTIMATE_DISABLE_WORKSPACE) return
4443
if (explicitName === undefined) return
4544

4645
const binding = await readLocalBinding(directory).catch(() => null)

‎packages/opencode/src/altimate/workspace/memory-sync.ts‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,9 @@ function sessionState(sessionID: string): SessionMemory {
106106
return state
107107
}
108108

109-
/** The mirror rides the workspace pilot flag and honours the memory opt-out.
110-
* Never active for anyone who has not opted into the pilot. */
109+
/** The mirror follows the workspace kill switch and honours the memory opt-out. */
111110
export function isEnabled(): boolean {
112-
return CoreFlag.ALTIMATE_WORKSPACE && !Flag.ALTIMATE_DISABLE_MEMORY
111+
return !CoreFlag.ALTIMATE_DISABLE_WORKSPACE && !Flag.ALTIMATE_DISABLE_MEMORY
113112
}
114113

115114
/** Test seam. Production leaves this unset and resolves the binding from the
@@ -821,7 +820,7 @@ export async function flushPendingMirrors(timeoutMs = 30_000): Promise<void> {
821820
}
822821

823822
/** Mirror one block. Safe to call unconditionally — returns immediately when
824-
* the pilot flag is off, the project is unbound, or the workspace has memory
823+
* workspaces are disabled, the project is unbound, or the workspace has memory
825824
* disabled. */
826825
export async function mirrorBlock(block: MemoryBlock, directory?: string): Promise<void> {
827826
if (!isEnabled()) return
@@ -1121,8 +1120,8 @@ function partitionPending(
11211120
* Deliberately shares ``partitionPending`` with the sweep rather than re-deriving
11221121
* the comparison: this number is a promise about what ``backfill`` would do.
11231122
*
1124-
* That promise includes the workspace's own memory setting, not just the pilot
1125-
* flag. ``backfill`` refuses outright when the bound workspace has memory off,
1123+
* That promise includes the workspace's own memory setting, not just the kill
1124+
* switch. ``backfill`` refuses outright when the bound workspace has memory off,
11261125
* so counting index misses in that state advertises a backlog no action can
11271126
* clear — a status line saying "14 not synced" above a sync that answers
11281127
* "memory is off for this project". Found end-to-end; both gates have to be the

0 commit comments

Comments
 (0)