Skip to content

feat: add developer-gated Prompt Shortcuts [risk:high] - #575

Open
Leeeon233 wants to merge 17 commits into
mainfrom
feat/prompt-shortcut-settings-sync
Open

feat: add developer-gated Prompt Shortcuts [risk:high]#575
Leeeon233 wants to merge 17 commits into
mainfrom
feat/prompt-shortcut-settings-sync

Conversation

@Leeeon233

@Leeeon233 Leeeon233 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Risk: 🔴 high | Confidence: medium — durable drafts and private/shared sync; local regression and adversarial reviews pass, live gateway and complete production-build validation remain open.

Prompt Shortcuts save scoped private or workspace-shared Prompts and insert frozen snapshots inline from /. They default off behind Settings → About → Developer mode → Beta features. Templates have no variables or expand-and-edit UI.

Settings → durable local working copy → immutable publication job → authorized index
Slash index → cancellable body read → inline snapshot → isolated draft checkpoint
Send → ordinary rewrites + snapshot segments → expanded Prompt and history spans
  • Reuse composer mentions in settings dialogs; forbid recursive Shortcuts, nested ACP commands and session references in templates. Other members' shared entries remain read-only.
  • Keep ACP caches separate, load bodies only on selection, and leave unverified dependencies visibly unavailable.
  • Preserve text order, ranges, snapshot Undo and account/workspace identity. Retired accepted submissions conditionally clear only their unchanged checkpoint.

This is a temporary developer-gated feature. Cleanup removed the obsolete prototype,
unused replacement/annotation APIs, duplicate compiler, dedicated Shortcut test suites
and Storybook stories. Existing general composer/mention coverage remains.
PR additions decreased from 14,009 to 6,635 lines; scope decreased from 122 to 84 files.

Validation

  • Latest cleanup changes only tests, stories and documentation. Component/shared typechecks,
    65 retained general composer/mention tests, formatting, docs and i18n passed.
  • Full workspace checks and ablation experiments passed before dedicated coverage was
    removed; these are historical evidence, not retained feature-specific coverage.
  • Production behavior, cancellation, snapshot Undo, identity fencing and storage protocols
    are unchanged by the test/story cleanup.

Remaining limits

  • Live dependency resolution, full conflict repair UI, cache eviction and real gateway Web/Electron E2E remain follow-up work. Optional sync requires matching host authorization/publication endpoints.
  • Production mobile bundling previously exhausted the Node heap. The standalone public E2E package is outside the outer installation graph and was excluded from lint after its missing Node-type error.
  • The outer affected-check wrapper is blocked by its pre-existing indexed OSS revision mismatch; checks were run separately. This PR does not commit the outer repository gitlink.

Leeeon233 and others added 13 commits September 5, 2026 12:41
Add scoped private/shared shortcut catalogs, lazy LoroDoc bodies, durable local publication recovery, settings authoring and Storybook prototypes. Slash invocation and composer send integration remain follow-up work.

Model: gpt-6-astra
…sk:high]

Settings now reads as the same surface as Agent Roles and MCP: one dialog
header/scroll/footer editor built from the shared `form-primitives` grammar,
Radix selects for the three scope axes, a Switch for sharing with its
visibility warning, and the shared catalog row. Adds scope pills, per-row
publication state, a read-only view for another member's shared Shortcut, and
disabled mention kinds that name the scope axes they still need.

Also carries this branch's local-first storage work: a separate local ledger,
write intents and immutable publication jobs, so a pending publication stays
durable without ever blocking Save or Delete.

Slash invocation, the variable argument tray and composer send integration
remain follow-up work.

Model: claude-opus-5
Scope moves out of its own section and sits inline above the prompt input,
each axis naming itself in its trigger, with the explanation folded into the
prompt hint. The description field says what it is and where it shows, a
variable's default sits beside its name instead of under it, and the prompt
box starts shorter.

Model: claude-opus-5
A default value is usually a few words, so the field starts at a single row
and tracks its content instead of reserving two. `AutoGrowTextarea` joins the
shared settings primitives, and re-measures on width changes because wrapping
is what decides the row count.

Model: claude-opus-5
Add independent Slash discovery, cancellable snapshot selection, inline invocation parameters, isolated draft recovery, and ordered prompt compilation with expand-and-edit support.

Model: gpt-6
Reuse the shared composer mention portal within the nearest modal and constrain desktop and mobile positioning to its bounds. Cover real Shortcut creation dialogs in Storybook and modal selection in regression tests.

Model: gpt-6
…sk:medium]

The tray now reads as the settings variable editor it mirrors: the `!{name}`
token beside its value, growing from one row, a missing value tinting its own
token instead of adding an asterisk. Desktop keeps a section header with the
dismiss action; mobile keeps the sheet with a full-width Done.

"Expand and edit" is removed. It existed to change a template's body, which
Settings already does — durably, for every invocation — and it paid for that
with a whole draft vocabulary of its own: generated unresolved markers, literal
ranges that stop substituted text being rescanned, their persistence and undo
rules, and an extra send-time check for leftover markers. A chip is atomic
again, so all of that goes with it.

Model: claude-opus-5
The catalog row was four left-aligned lines against an empty right half. It is
now two: identity reads down the left, and what the author set — scope, variable
count — plus what is happening to it sit against the right edge. Both halves
wrap rather than switching on a viewport breakpoint, because settings render in
a panel far narrower than the window. Status copy shortens to phrases that fit
that column.

The tray drops the "fill these variables" line between the prompt and the
fields and sits against the invocation instead. The chip's own badge and the
tinted token already name what is missing; a third sentence only pushed the
fields away from the thing they belong to.

Model: claude-opus-5
A Shortcut carries an optional `emoji`, normalized on the way in and projected
into the index so the list can show it without loading a body. It replaces the
`/` glyph in the catalog tile; unset renders the default rather than an empty
slot. The emoji control itself moves out of the Agent Role editor into
`settings/emoji-field.tsx`, so the two editors cannot grow two of them.

Also drops the `!N` badge painted over an invocation chip — the chip already
turns red and the tray names the values — and with it the mention primitive's
`badge` slot, which had no other caller.

While a parameter tray is open the composer stops reserving its blank writing
rows: those rows are room for writing, and reserving them above the tray left a
band of empty box between the `/command` and the fields it belongs to.

Model: claude-opus-5
…k:medium]

Four things the composer and the catalog were getting wrong:

Clearing the prompt to empty dropped focus. The reset remounts the mention tree
to re-arm the hydrators, which replaces the textarea node; restoring focus was
conditional on the caller having passed a ref, and the settings template editor
passes none. The component keeps its own ref now and hands the node to the
forwarded one as well.

The catalog reported background publication. A local save is already durable and
the runtime retries on its own, so neither state is something a reader can act on
from a row. The only status left is the one its author must repair: a reference
that no longer fits the saved scope.

The `/` menu shows each Shortcut's own emoji, the way an Agent Role's row does.

A chip now carries the values that were filled into it (`shortcutChipText`,
applied on tray close), capped by code points so a truncation cannot split a
surrogate pair. A draft of three identical `/review` chips was not something
anyone could check before sending. Both forms of the text stay valid, so a draft
stored before its values still restores.

Model: claude-opus-5
A Shortcut is a Prompt and a scope now, nothing else. `!{name}` is ordinary
text: stored, expanded and sent exactly as written.

Gone from the model: the `variables` field, its schema and byte limits, the
index's `variableCount`, placeholder parsing and escaping, value injection, the
`missing_variables` error code, and `updateShortcutInvocation` (which existed
only to migrate values across a reload). `ShortcutInvocation` is `{ id,
snapshot }`, so `expandShortcut` is a straight lowering of the snapshot's text
and mentions.

Gone from the UI: the Settings variables section, the composer's parameter tray
and bottom sheet, their send gate, the chip's missing-count colouring, and the
value label a chip carried. What remains of the composer bridge is
`shortcut-invocation-status.tsx` — why an inserted chip cannot be sent, which is
about live context rather than anything the caller types.

The invocation schema is deliberately not strict: a draft stored while values
existed still restores, minus the ignored key.

Model: claude-opus-5
Default the opt-in off and share the developer-mode gate across settings navigation, direct panel access, mention selection and the workspace runtime. Preserve saved data and cover toggle transitions and late selection results.

Model: gpt-6
Clear the Shortcut checkpoint before successful landing navigation, fenced by composer mount and draft identity so late session acceptance cannot delete or navigate a replacement draft. Add deterministic recovery regressions and satisfy the branch formatting gate.

Model: gpt-6
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T06:00:26.392316Z 665a216 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Preserve composer submission ownership and Shortcut preparation while integrating main. Clear retired accepted Shortcut checkpoints only when their submitted version remains current.

Model: gpt-6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 362a08f32b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +375 to +379
await this.put({
entry,
published: current?.operation ? current.published : entry,
operation: 'delete',
deleted: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Revoke the published body on delete

When an author deletes a successfully published Shortcut from the authoring device, entry is the local working projection, while preparePublication() uploaded a fresh body and stored that distinct projection in current.published. Persisting the caller's working entry here therefore makes publishRecord() call revokeShortcut with a bodyDocId that was never activated; a host enforcing the request's body/visibility fence cannot revoke it, so a shared Shortcut remains accessible and every retry repeats the wrong request. Enqueue the published projection for remote deletion while retaining the local deletion state.

AGENTS.md reference: packages/shared/src/prompt-shortcuts/AGENTS.md:L22-L25

Useful? React with 👍 / 👎.

await this.recover();
const existing = this.publication(id);
const current = this.get(id);
if (existing && !current?.deleted) return existing;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve an existing publication job through deletion

If Delete is selected while a save/activation job is pending or its reply was lost, current.deleted makes this condition skip the durable existing job; the method then overwrites the publication row with the delete record below. Consequently PromptShortcutRuntime.flush() never sees the obsolete save job and never calls settle(), losing the only identity capable of fencing a staged or already-active body and potentially leaving the supposedly deleted publication or its quota reservation behind. Preserve and settle the existing job before replacing it with deletion.

AGENTS.md reference: packages/shared/src/prompt-shortcuts/AGENTS.md:L36-L37

Useful? React with 👍 / 👎.

Remove the superseded design prototype, unused editable annotations and replacement API, test-only composer compiler and publication helper. Keep real component stories and move stale/duplicate invocation assertions onto the actual send path. Consolidate repeated documentation and preserve locale ordering.

Ablations retain semantic Undo and stale-preparation fences: negative controls fail when either protection is removed. Full workspace typecheck and test:ci pass; installed-workspace lint, formatting, docs, i18n and boundary checks pass.

Model: gpt-6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a4e4e11477

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

<StuckConnectionBannerContainer />
{workspaceReady ? <DesktopSettingsModal /> : null}
</WorkspaceRuntimeShell>
<PromptShortcutProvider enabled={workspaceReady}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate the Shortcut runtime on the workspace window owner

When Prompt Shortcuts are enabled in Electron, every primary or auxiliary window mounts this provider even though owner already identifies the single workspace window permitted to run singleton background work. Opening a second window for the same workspace therefore creates another PromptShortcutRuntime over the same account/workspace IndexedDB and lets both instances drain the same publication outbox, causing competing ledger writes and duplicate or conflicting cloud publication attempts. Mount the runtime only for the workspace owner or provide a genuinely shared single owner.

AGENTS.md reference: packages/components/src/providers/AGENTS.md:L41-L47

Useful? React with 👍 / 👎.

Comment on lines +53 to +55
getCandidates: (term, limit) =>
selectPromptShortcutCandidates({ entries, context, loading }, term, t, limit).map(
(candidate) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wire live dependency resolution into Shortcut discovery

For every Shortcut containing a file, Skill, Role, issue, or PR, this production call omits resolveDependency, so selectPromptShortcutCandidates uses unverifiedShortcutDependency and always marks the entry unknown; non-exact matches disappear and exact matches are disabled. The selection, status, and compilation paths likewise never provide a resolver, so users can author reference-bearing Shortcuts but can never insert or send them even when all dependencies are available. Supply the live dependency resolver throughout this composer path.

AGENTS.md reference: packages/components/src/components/mentions/AGENTS.md:L114-L116

Useful? React with 👍 / 👎.

…edium]

Remove dedicated Shortcut suites, stories and feature-only additions to shared fixtures. Retain existing general composer and mention tests, and document the temporary coverage scope without changing production behavior.

Model: gpt-6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3aca38b3f9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +307 to +308
if (trigger === '/') {
const directCategories = categories.filter((category) => category.directTrigger === '/');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Hide ACP commands from inline slash menus

When Prompt Shortcuts are enabled and the composer already contains ordinary text, / remains active for inline Shortcut insertion, but this branch also includes every ACP command category. Selecting one then replaces only the slash-to-caret query, producing text such as explain this /review; the agent receives an ordinary message instead of the advertised whole-prompt command. Filter ACP commands out whenever the slash trigger is not the entire prompt.

AGENTS.md reference: packages/components/src/components/mentions/AGENTS.md:L117-L118

Useful? React with 👍 / 👎.

Comment on lines +4 to +5
export function promptShortcutDatabaseName(workspaceId: string, userId: string): string {
return `${PROMPT_SHORTCUT_DATA_PREFIX}${encodeURIComponent(workspaceId)}:${encodeURIComponent(userId)}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register Shortcut databases for hard-reset fallback

On engines where indexedDB.databases() is unavailable or throws, a crash-screen hard reset cannot discover this new account/workspace-specific database name. clearAllLodyLocalData() only falls back to static known names, and the later RuntimeProvider extras contain only the workspace repo and cursor databases, so saved Prompt bodies and outbox data survive the promised full wipe/sign-out. Record these dynamic database names for the hard-reset fallback or pass them explicitly before identity storage is cleared.

AGENTS.md reference: packages/components/src/lib/AGENTS.md:L34-L37

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant