feat(secrets): add optional descriptions to workspace secrets (#6796)

* feat(secrets): add optional descriptions to workspace secrets

Workspace secrets already have a backing credential row with a description
column, but nothing surfaced it. Teammates had no way to record what a
secret is for.

- Add a Description field to the secret detail page, matching the
  integrations credential page, gated on workspace-secret admin
- Fold the value and description editors into one Save/Discard pair and
  one unsaved-changes guard; two guards cannot coexist, since each seeds
  its own same-URL history entry
- Match descriptions in the secrets settings search
- Expose description on GET/PUT /api/v2/secrets and in the CLI

Descriptions are workspace-only: env_personal credential rows are
per-workspace mirrors of one user-global secret, so one saved there would
exist in a single workspace, and a personal secret has no teammates to
inform. The API rejects a description on personal scope rather than
silently dropping it, and omitting it on PUT leaves any existing
description untouched so a value rotation cannot erase it.

* fix(secrets): address review findings on secret descriptions

- Patch the credential detail cache optimistically on update. `onMutate`
  cancelled the detail query but only patched the lists, so a detail-backed
  editor stayed dirty after a successful save until the refetch landed —
  long enough for Discard to restore the pre-save value over the committed
  one, and for Back to open the unsaved-changes guard.
- Memoize `useSecretValue`'s returned callbacks and object, per the hook
  convention, so the composed form's save/discard stop churning per render.
- Reject a description on a personal secret in the domain layer rather than
  only at the v2 boundary. The internal credential update path accepted one
  for any type, writing data every reader hides.
- Normalize an empty description to null so the API and UI agree.
- Correct the secrets documentation, which described a Display Name field
  the detail view does not have and omitted the scope rule.
- Drop the CLI's copy of the 500-character bound; it can't import the
  contract, so a copy only drifts from the message the API already returns.
- Collapse a redundant save guard and align the description write gate with
  the render gate.

Leaves the integrations credential page byte-identical to staging.

* fix(secrets): keep the API docs example and CLI column order stable

Backward-compatibility fixes for anyone who never sets a description.

- Move the blank-to-null normalization out of the contract and into the
  route. A Zod `.transform()` on any property drops the whole request
  schema's OpenAPI examples, which had silently removed the Set Secret
  request example from the published docs.
- Append the CLI `description` column instead of inserting it before
  `updated`. `--output text` is positional, so inserting would shift every
  field an existing script cuts.
- Reject a description on a personal secret with a message that says so,
  rather than dropping the field and falling through to the generic
  "no updatable fields" error.
This commit is contained in:
Waleed
2026-08-17 17:33:35 -07:00
committed by GitHub
parent 3abac09dc3
commit 0b4d34137b
24 changed files with 574 additions and 63 deletions
@@ -1787,6 +1787,7 @@ sim secrets set <name> [options]
| --- | --- | --- |
| `--scope <scope>` | Yes | Secret ownership scope. Accepted values: `workspace`, `personal`. |
| `--value <value>` | No | Secret value; visible to shell history when supplied directly. |
| `--description <description>` | No | What the secret is for, shown to teammates; workspace scope only. Omit to leave an existing description unchanged. |
</CommandTable>
@@ -80,5 +80,6 @@ sim secrets set <name> [options]
| --- | --- | --- |
| `--scope <scope>` | Yes | Secret ownership scope. Accepted values: `workspace`, `personal`. |
| `--value <value>` | No | Secret value; visible to shell history when supplied directly. |
| `--description <description>` | No | What the secret is for, shown to teammates; workspace scope only. Omit to leave an existing description unchanged. |
</CommandTable>
@@ -95,14 +95,15 @@ Click **Details** on any secret row to open its detail view.
<Image
src="/static/secrets/secret-details.png"
alt="Secret details view showing Display Name, Description, and Members sections"
alt="Secret details view showing Key, Value, Description, and Members sections"
width={700}
height={400}
/>
From here you can:
- Edit the **Display Name** and **Description**
- View the **Key** and edit the **Value**
- Edit the **Description** — an optional note telling teammates what the secret is for. Workspace secrets only; a personal secret is not shared, so it has none
- Manage **Members** — invite teammates by email and assign them an **Admin** or **Member** role
Click **Save** to apply changes, or **Back** to return to the list.