Files
coder/docs/admin/templates/startup-coordination/usage.md
T
Nick Vigilante c84aa564ba docs: normalize code-fence languages for Shiki compatibility (#27161)
Normalizes non-standard code-fence language tags across `docs/**` so a
strict highlighter (Shiki, used by Fumadocs) won't fail the build on an
unrecognized language, and unifies redundant synonym tags onto one
canonical form per language. The current renderer (Speed-Highlight)
detects the language from the code content, not the fence label, so this
drift wasn't visible until now.

## Changes

- `hcl` -> `tf` (199 fences, including indented ones nested in
numbered/bulleted lists). Shiki ships `hcl` and `terraform` as two
distinct grammars (not aliases); every `hcl`-tagged fence in `docs/**`
is actually Terraform resource/data/provider syntax, so the more
specific `terraform` grammar is correct for all of them. `tf` is Shiki's
own alias for that grammar, and it's also what GitHub's own markdown
renderer resolves to the same HCL/Terraform highlighting.
- `pwsh`/`powershell` -> `ps1`. Both `ps` and `ps1` are registered
PowerShell aliases in Shiki, but on GitHub's renderer only `.ps1` is a
registered file extension (`.ps` isn't), so `ps1` renders identically to
`powershell` there today while bare `ps` would silently lose
highlighting.
- `env` -> `dotenv` (a dedicated Shiki grammar for `KEY=VALUE` files)
- `text`/`output`/`none`/`url` -> `txt`. Same built-in plain-text
fallback either way, just shorter.
- `Dockerfile` -> `dockerfile` (lowercase)
- `bash`/`shell` -> `sh` (732 fences). Shiki and GitHub both alias all
three to a single shell grammar; this was already the style guide's
stated preference, just not enforced across the existing corpus until
now.
- `markdown` -> `md` (4 fences). Alias of the same grammar in both Shiki
and GitHub.
- `jsonc` -> `json` (1 fence). The block has no comments or trailing
commas, so it doesn't need the comments-capable grammar.
- `ts` -> `tsx` (2 fences, `docs/about/contributing/frontend.md`).
Verified the actual content tokenizes identically under both grammars,
and a sibling block in the same file already needs `tsx` for real JSX,
so unifying to one tag is safe for this file. Documented a caveat: `tsx`
mis-tokenizes the legacy angle-bracket type-assertion syntax
(`<Type>value`), which is invalid in real `.tsx` files anyway, so use
`value as Type` instead.
- `yml` -> `yaml` (1 fence)
- Updated `docs/.style/style-guide/formatting.md` to document all
canonical tags

`promql` (2 fences) and `caddyfile` (2 fences) are left as-is. Shiki
doesn't bundle a grammar for either, so they need a custom grammar
registration when the site adopts Shiki, rather than degrading to `txt`.
Tracked as follow-up work under DOCS-118 and
[DOCS-544](https://linear.app/codercom/issue/DOCS-544/vendor-a-local-promql-grammar-for-shiki-syntax-highlighting)
(promql).

Does not touch `offlinedocs/`.

Linear:
[DOCS-476](https://linear.app/codercom/issue/DOCS-476/normalize-docs-code-fence-languages-de-risk-shikifumadocs)

<details>
<summary>How the fence tags were verified</summary>

Each tag was tested against a real `shiki@latest` highlighter instance
(`codeToHtml`/`codeToTokens`) and cross-checked against GitHub's
`@wooorm/starry-night` grammar sources (the renderer that actually
displays these `.md` files today, in repo browsing and PR diffs), since
that's what determines whether brevity is safe before Shiki adoption:

```text
FAIL  env        -- Language `env` is not included in this bundle.
FAIL  Dockerfile -- Language `Dockerfile` is not included in this bundle.
FAIL  promql     -- Language `promql` is not included in this bundle.
FAIL  caddyfile  -- Language `caddyfile` is not included in this bundle.
FAIL  pwsh       -- Language `pwsh` is not included in this bundle.
FAIL  output     -- Language `output` is not included in this bundle.
```

`hcl` doesn't error in Shiki, since it's a real grammar, but that's
exactly the trap: it was silently rendering every fence with the generic
HCL grammar instead of the Terraform-specific one. Every `hcl`-tagged
fence in `docs/**` was manually checked against `origin/main` and is
genuinely Terraform content.

For `ts`/`tsx`, tokenizing the actual doc content confirmed identical
output under both grammars; a synthetic test with the legacy
angle-bracket cast syntax confirmed `tsx` degrades on that specific
construct, which the style guide now calls out.

The first normalization pass only matched fence tags at column 0
(`^```tag$`), missing tags indented inside numbered/bulleted lists. A
follow-up pass caught the remaining occurrences at any indentation
level.

</details>


---

*This PR description and the underlying changes were prepared with Coder
Agents assistance.*
2026-07-15 14:07:09 -04:00

6.7 KiB

Workspace Startup Coordination Usage

Note

This feature is experimental and may change without notice in future releases.

Startup coordination is built around the concept of units. You declare units in your Coder workspace template using the coder exp sync command in coder_script resources. When the Coder agent starts, it keeps an in-memory directed acyclic graph (DAG) of all units of which it is aware. When you need to synchronize with another unit, you can use coder exp sync start $UNIT_NAME to block until all dependencies of that unit have been marked complete.

What is a unit?

A unit is a named phase of work, typically corresponding to a script or initialization task.

  • Units may declare dependencies on other units, creating an explicit ordering for workspace initialization.
  • Units must be registered before they can be marked as complete.
  • Units may be marked as dependencies before they are registered.
  • Units must not declare cyclic dependencies. Attempting to create a cyclic dependency will result in an error.

Requirements

Important

The coder exp sync command is only available from Coder version >=v2.30 onwards.

To use startup dependencies in your templates, you must:

  • Modify your workspace startup scripts to run in parallel
  • Declare dependencies as required using coder exp sync

Declare Dependencies in your Workspace Startup Scripts

Single Dependency

Here's a simple example of a script that depends on another unit completing first:

#!/bin/bash
UNIT_NAME="my-setup"

# Declare dependency on git-clone
coder exp sync want "$UNIT_NAME" "git-clone"

# Wait for dependencies and mark as started
coder exp sync start "$UNIT_NAME"

# Do your work here
echo "Running after git-clone completes"

# Signal completion
coder exp sync complete "$UNIT_NAME"

This script will wait until the git-clone unit completes before starting its own work.

Multiple Dependencies

If your unit depends on multiple other units, you can declare all dependencies before starting:

#!/bin/bash
UNIT_NAME="my-app"
DEPENDENCIES="git-clone,env-setup,database-migration"

# Declare all dependencies
if [ -n "$DEPENDENCIES" ]; then
  IFS=',' read -ra DEPS <<< "$DEPENDENCIES"
  for dep in "${DEPS[@]}"; do
    dep=$(echo "$dep" | xargs)  # Trim whitespace
    if [ -n "$dep" ]; then
      coder exp sync want "$UNIT_NAME" "$dep"
    fi
  done
fi

# Wait for all dependencies
coder exp sync start "$UNIT_NAME"

# Your work here
echo "All dependencies satisfied, starting application"

# Signal completion
coder exp sync complete "$UNIT_NAME"

Inspect Unit State

Use coder exp sync list to see all registered units and their current state:

coder exp sync list

Example output:

UNIT           STATUS     READY
git-clone      completed  true
env-setup      started    true
ide-configure  pending    false

To inspect a single unit and its dependencies in detail, use coder exp sync status <unit>.

To verify the agent socket is reachable, use coder exp sync ping.

Best Practices

Test your changes before rolling out to all users

Before rolling out to all users:

  1. Create a test workspace from the updated template
  2. Check workspace build logs for sync messages
  3. Verify all units reach "completed" status using coder exp sync list
  4. Test workspace functionality

Once you're satisfied, promote the new template version.

Handle missing CLI gracefully

Not all workspaces will have the Coder CLI available in $PATH. Check for availability of the Coder CLI before using sync commands:

if command -v coder > /dev/null 2>&1; then
  coder exp sync start "$UNIT_NAME"
else
  echo "Coder CLI not available, continuing without coordination"
fi

Complete units that start successfully

Units must call coder exp sync complete to unblock dependent units. Use trap to ensure completion even if your script exits early or encounters errors:


SYNC_STARTED=0
if coder exp sync start "$UNIT_NAME"; then
  SYNC_STARTED=1
fi

cleanup_sync() {
  if [ "$SYNC_STARTED" -eq 1 ]; then
    coder exp sync complete "$UNIT_NAME"
  fi
}
trap cleanup_sync EXIT

Use descriptive unit names

Names should explain what the unit does, not its position in a sequence:

  • Good: git-clone, env-setup, database-migration
  • Avoid: step1, init, script-1

Prefix a unique name to your units

When using coder exp sync in modules, note that unit names like git-clone might be common. Prefix the name of your module to your units to ensure that your unit does not conflict with others.

  • Good: <module>.git-clone, <module>.claude
  • Bad: git-clone, claude

Document dependencies

Add comments explaining why dependencies exist:

resource "coder_script" "ide_setup" {
  # Depends on git-clone because we need .vscode/extensions.json
  # Depends on env-setup because we need $NODE_PATH configured
  script = <<-EOT
    coder exp sync want "ide-setup" "git-clone"
    coder exp sync want "ide-setup" "env-setup"
    # ...
  EOT
}

Avoid circular dependencies

The Coder Agent detects and rejects circular dependencies, but they indicate a design problem:

# This will fail
coder exp sync want "unit-a" "unit-b"
coder exp sync want "unit-b" "unit-a"

Frequently Asked Questions

How do I identify scripts that can benefit from startup coordination?

Look for these patterns in existing templates:

  • sleep commands used to order scripts
  • Using files to coordinate startup between scripts (e.g. touch /tmp/startup-complete)
  • Scripts that fail intermittently on startup
  • Comments like "must run after X" or "wait for Y"

Will this slow down my workspace?

No. The socket server adds minimal overhead, and the default polling interval is 1 second, so waiting for dependencies adds at most a few seconds to startup. You are more likely to notice an improvement in startup times as it becomes easier to manage complex dependencies in parallel.

How do units interact with each other?

Units with no dependencies run immediately and in parallel. Only units with unsatisfied dependencies wait for their dependencies.

How long can a dependency take to complete?

By default, coder exp sync start has a 5-minute timeout to prevent indefinite hangs. Upon timeout, the command will exit with an error code and print timeout waiting for dependencies of unit <unit_name> to stderr.

You can adjust this timeout as necessary for long-running operations:

coder exp sync start "long-operation" --timeout 10m

Is state stored between restarts?

No. Sync state is kept in-memory only and resets on workspace restart. This is intentional to ensure clean initialization on every start.