From e3420b39d1f5bb531f26d94465f7aa3b277baf29 Mon Sep 17 00:00:00 2001 From: Bonanza Date: Thu, 13 Aug 2026 19:30:12 -0700 Subject: [PATCH] =?UTF-8?q?fix(teaching):=20manifest=20actions=20ARE=20dis?= =?UTF-8?q?coverable=20+=20textField=20reads=20'text'=20=E2=80=94=20drop?= =?UTF-8?q?=20two=20stale=20host=20caveats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two shipped-host behaviors were still taught as bugs/workarounds: 1. Manifest 'actions.definition' discovery (stale fn-163 caveat). The host's ActionsRegistryCorePlugin replays manifest contributions into DiscoveryStore at cold start and on plugin activation (replayManifestDefinitionContributions, ActionsRegistryCorePlugin.swift ~739-749 cold start / ~511-522 re-activation), and ActionsRegistryCorePluginTests verifies the stubs surface via the merged actions.all() / palette.query() catalog. Teaching now retains the merged-catalog contract: manifest-only entries ARE discoverable (badged "manifest only"), and become executable once runtime register() binds the handler. Dual registration stays the pattern — the rationale is now "manifest = discoverable metadata, runtime = executable", not "manifest never reaches discovery". 2. textField initial contents (obsolete 'value' workaround). The host reads properties["text"] first with legacy properties["value"] as back-compat fallback only (PluginViewDescriptorRenderer.swift ~230-232, fn-179 commit b18064e85). Teaching now documents 'text' as the supported property and removes the assertion-cast dual-key workaround. Touched: extension-api.md (both flagged regions + all() table row + palette paragraph + extensions[] blockquote), patterns.md par.3, SKILL.md, viewdescriptor-authoring/SKILL.md, plugin-architect.md, new-plugin.md, README.md, plus regenerated compiled/ artifacts (npm run check green; compiled context 179067 bytes, under the 179200-byte ceiling). --- README.md | 2 +- plugins/appos-dev/agents/plugin-architect.md | 2 +- plugins/appos-dev/commands/new-plugin.md | 4 +- plugins/appos-dev/compiled/manifest.json | 10 +-- .../compiled/plugin-factory-context.md | 67 +++++++++---------- .../skills/appos-plugin-dev/SKILL.md | 9 +-- .../reference/extension-api.md | 46 ++++++------- .../appos-plugin-dev/reference/patterns.md | 12 ++-- .../skills/viewdescriptor-authoring/SKILL.md | 2 +- 9 files changed, 76 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index f554119..2388ffd 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A Claude Code plugin for creating, building, testing, and deploying [AppOS](http v3.0 re-anchors every teaching surface on the published SDK 3.0.0 (`@appos.space/plugin-types@3.0.0`), the surface the shipped AppOS 1.0.0 host actually exposes. The plugin's own version is deliberately aligned with the SDK major it teaches (it is NOT a `minHostVersion` — that stays `"1.0.0"`). Key changes: - **Full 3.0.0 API surface** — 43 namespaces on `PluginContext` (of which 21 core-plugin namespaces: actions, palette, scheduler, vault, store, resources, tokens, bundles, entities, fields, ledger, views, surfaces, protocols, notifications, input, webhook, llm, recipes, sequences, fileSystem) and the 135-scope canonical permission model with 5 legacy aliases (deprecated). -- **`extensions[]` manifests** — manifest-declarative contributions to core-plugin extension points, including the required `actions.definition` dual-registration pattern: today the manifest entry is catalog/manifest metadata only (host bug fn-163), and the runtime `ctx.actions.register()` call is what provides discovery and execution — see `plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md`. +- **`extensions[]` manifests** — manifest-declarative contributions to core-plugin extension points, including the required `actions.definition` dual-registration pattern: the manifest entry is replayed into discovery at cold start and on plugin activation (visible in `ctx.actions.all()` / palette, badged "manifest only"), and the runtime `ctx.actions.register()` call is what binds the executable handler — see `plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md`. - **Scaffold pins `^3.0.0`** — `new-plugin` scaffolds depend on the 3.x SDK line; the SDK main entry ships no ambient globals, so all types are imported from the packages (3.0.1+ adds one opt-in globals subpath typing the host-injected `URL`; scaffolds declare that surface locally in the `src/jsc-globals.ts` `declare global` module instead — a `.ts` module, so it stays type-checked even under the scaffold's `skipLibCheck: true`). - **Byte-verbatim type mirror + drift gate** — the bundled d.ts reference is a generated mirror of the published npm tarball, and CI type-checks every fenced code example against it (see "Knowledge verification" below). diff --git a/plugins/appos-dev/agents/plugin-architect.md b/plugins/appos-dev/agents/plugin-architect.md index 3e6a37d..c800280 100644 --- a/plugins/appos-dev/agents/plugin-architect.md +++ b/plugins/appos-dev/agents/plugin-architect.md @@ -90,7 +90,7 @@ When the user describes what they want to build, map their requirements to speci - `interPluginEvents` — Pub/sub between plugins **Actions & automation (core-plugin tier)** -- `actions` — Public Action Fabric: typed, schema-validated, policy-bearing public actions (`register`, `invoke`, `all`, `registerFromCommand`). Declare actions in the manifest `extensions[]` (`actions.definition`) too, but ALWAYS pair with the runtime registration — manifest-declared actions don't reach discovery on their own yet (host bug fn-163; see `skills/appos-plugin-dev/reference/extension-api.md`): today the manifest entry is catalog/manifest metadata, and the runtime `ctx.actions.register(...)` call is what makes the action discoverable and executable. +- `actions` — Public Action Fabric: typed, schema-validated, policy-bearing public actions (`register`, `invoke`, `all`, `registerFromCommand`). Declare actions in the manifest `extensions[]` (`actions.definition`) too, but ALWAYS pair with the runtime registration — the manifest entry is replayed into discovery at cold start and on plugin activation (visible in `ctx.actions.all()` / `palette.query()`, badged "manifest only"; see `skills/appos-plugin-dev/reference/extension-api.md`), and the runtime `ctx.actions.register(...)` call is what makes the action executable (`ACTION_NOT_FOUND` on invoke until it binds). - `palette` — Command palette integration for public actions (`query`, `history`, `pin`) - `scheduler` — Job scheduling engine: interval/cron/notification/fsEvents/calendar/power/network triggers, conditions, run history - `recipes` / `sequences` — Author-declared multi-step plans (linear or LLM-agent) dispatched through the action fabric diff --git a/plugins/appos-dev/commands/new-plugin.md b/plugins/appos-dev/commands/new-plugin.md index 70c71b6..ac23451 100644 --- a/plugins/appos-dev/commands/new-plugin.md +++ b/plugins/appos-dev/commands/new-plugin.md @@ -266,9 +266,9 @@ If the plugin exposes public actions (command palette, automation), add an `exte ] ``` -**Dual registration is required.** Manifest-declared actions don't reach discovery on their own yet (host bug fn-163; see `skills/appos-plugin-dev/reference/extension-api.md`): an `actions.definition` contribution alone currently never becomes palette-visible or invokable — no cold-start palette entry, no `ctx.actions.all()` stub, no Settings → Actions row. Today the manifest entry is catalog/manifest metadata (visible in catalogs and manifest scans), not runtime discovery. Pair EVERY `actions.definition` contribution with a runtime `ctx.actions.register(...)` or `ctx.actions.registerFromCommand(...)` call in `activate()` using the same id — the runtime registration is what makes the action discoverable and executable. Ship BOTH, exactly as `appos-plugin-ytdlp` does. +**Dual registration is required.** An `actions.definition` contribution is discoverable metadata (see `skills/appos-plugin-dev/reference/extension-api.md`): the host replays it into the discovery catalog at cold start and on plugin activation, so it surfaces as a `ctx.actions.all()` stub, a palette entry, and an Action Browser row badged "manifest only" — but it is NOT executable: invoking it surfaces `ACTION_NOT_FOUND` until a runtime `ctx.actions.register(...)` or `ctx.actions.registerFromCommand(...)` call in `activate()` binds the handler using the same id. Ship BOTH, exactly as `appos-plugin-ytdlp` does. -**Removal marker**: when you retire an action, remove BOTH sites — the runtime `register()` call and the manifest contribution. A leftover manifest stub is stale catalog/manifest metadata today, and once fn-163 lands it would be replayed into discovery at every cold start as a permanently non-executable palette entry. +**Removal marker**: when you retire an action, remove BOTH sites — the runtime `register()` call and the manifest contribution. A leftover manifest stub is replayed into discovery at every cold start as a permanently non-executable palette entry (`ACTION_NOT_FOUND` on invoke). ## 9. Write src/main.ts diff --git a/plugins/appos-dev/compiled/manifest.json b/plugins/appos-dev/compiled/manifest.json index fff39ad..d0dd916 100644 --- a/plugins/appos-dev/compiled/manifest.json +++ b/plugins/appos-dev/compiled/manifest.json @@ -2,13 +2,13 @@ "schema": 1, "artifacts": { "cli-chat-system-prompt.md": "0ace5e39d569fac84ca22e244bfb5dae8242b14e059395894f16f712b1ecc170", - "plugin-factory-context.md": "389a4fc99cdc0fee665ac63bc1600332865b6f3fd7ff5314bd18a54065bf5bec" + "plugin-factory-context.md": "2624e52479f40e97d9555a8b20f6c9a73cc32916d7776ce03c44f740e4cdd5b6" }, "sources": { - "SKILL.md": "f4868a887ab7726ca8c6463b90953a1efb82712fb1a3df5e68888f9b43644cd6", - "reference/extension-api.md": "b37b24cd2a5e02345b361ee08aea29a885084ebfb4a8a73358036a91572c49e8", - "reference/patterns.md": "15be968192b19bbd74735a03571322df1a7c4fece579c6d2b3105eabb270f40a", - "reference/plugin-api/index.d.ts": "cbdd7a4aa96001d3fe664427ffc30039ce09ba80dca2eee32960c5c925c4d8d4", + "SKILL.md": "9ef85656be1a2f268a4ba52bc0a06413124732ba51f604d70a726419a6e873bc", + "reference/extension-api.md": "41c6224b238db928fcd22a912914d12048f65a21dd7fb7e437de123f6b439e10", + "reference/patterns.md": "3d70e16a476145c2d72e2893af5a2c7cafb07ee7f280ef62de91d8e7ddcc691c", + "reference/plugin-api/index.d.ts": "7299f52226d577aa8ad6160357a8dee2e02f90bb19a18f93ec31d605f5756b27", "reference/plugin-api/core.d.ts": "3e951ef56e6148879fbe92d592399711f4bad5556f1549233ec3d32d0d9c49f9", "reference/plugin-api/views.d.ts": "323f8570fe1c0e6140b5ee1f46cf79f368dc4d920e4b14544e5bf5753ae05f1e", "reference/plugin-api/namespaces.d.ts": "467e6374fced48f027c22e9d01fe6daa68839b2f9881726725b901c66ccc8b1f", diff --git a/plugins/appos-dev/compiled/plugin-factory-context.md b/plugins/appos-dev/compiled/plugin-factory-context.md index febb26d..8455959 100644 --- a/plugins/appos-dev/compiled/plugin-factory-context.md +++ b/plugins/appos-dev/compiled/plugin-factory-context.md @@ -223,9 +223,10 @@ void token; // keep for ctx.actions.unregister(token) on dispose for an LLM reader. `registerFromCommand(commandId, metadata)` projects an existing command into the catalog. - Declare actions in the manifest `extensions[]` too, but ALWAYS pair with - the runtime registration — manifest-declared actions don't reach - discovery on their own yet (host bug fn-163; see - `reference/extension-api.md`). + the runtime registration — the manifest entry is replayed into discovery + (visible in `all()` / `palette.query()`, badged "manifest only") but + invoking it surfaces `ACTION_NOT_FOUND` until the runtime `register()` + binds the handler (see `reference/extension-api.md`). - Scopes: `actions.register` to register, `actions.invoke` to invoke (`actions.invoke.agent` additionally for agent-sourced invokes). @@ -442,7 +443,7 @@ load. Full pattern: `reference/patterns.md` §13 + §23. Two manifest families live in `reference/extension-api.md`: `extensions[]` (manifest-declarative core-plugin contributions — qualified-id grammar, -per-EP payloads, fn-163 dual-registration caveat for actions) and +per-EP payloads, dual-registration contract for actions) and `dependencies` (system binaries + plugin deps; full example: `reference/patterns.md` §23). @@ -711,22 +712,21 @@ Other methods: |---|---|---| | `registerFromCommand(commandId, metadata)` | Projects an existing `ctx.commands` command into the action catalog | `actions.register` | | `invoke(id, input, source?)` | Invokes through the full pipeline; resolves to an `ActionReceipt` | `actions.invoke` (+ `actions.invoke.agent` for source `"agent"`) | -| `all()` | Lists the action catalog (runtime-registered only on the shipped host — see below) | `actions.list` | +| `all()` | Lists the merged action catalog (runtime-registered + manifest-declared — see below) | `actions.list` | | `unregister(handleToken)` | Removes an executable registration | `actions.register` | -By design `all()` also returns **manifest-only** entries (declared via -`extensions[]` but with no executable handler bound yet; invoking one -surfaces `ACTION_NOT_FOUND` until `register()` binds the handler). On the -shipped host this does NOT happen — manifest `actions.definition` -contributions never reach discovery (host bug fn-163; see the caveat -under "`extensions[]`" below), so `all()` and `palette.query()` list -runtime-registered actions only. Never write code that expects to find an -unbound manifest action in `all()`. +`all()` also returns **manifest-only** entries — `actions.definition` +contributions are replayed into discovery at cold start and on plugin +activation, so they surface in `all()` and `palette.query()` (badged +"manifest only" in the Action Browser) before any runtime call. They are +metadata, not executables: invoking one surfaces `ACTION_NOT_FOUND` until +`register()` / `registerFromCommand()` binds the handler (see +"`extensions[]`" below). #### `ctx.palette` — palette integration (fn-89) -`query(text, scope?)` searches the action catalog (the fn-163 caveat -above applies — runtime-registered actions only); `pin(id)` / +`query(text, scope?)` searches the merged action catalog (manifest-only +entries included — see `all()` above); `pin(id)` / `unpin(id)` manage palette pins (`palette.contribute.scope`); `history(limit?)` returns recent invocations (`palette.history`). @@ -979,12 +979,14 @@ conventionally under `contribution`: AppOS repo — its manifest carries recipe + sequence + trigger contributions and its `activate` is a no-op. -> **Caveat — manifest-declared ACTIONS don't reach discovery yet (host bug -> fn-163).** An `actions.definition` contribution alone currently never -> becomes palette-visible or invokable. Pair EVERY `actions.definition` -> contribution with a runtime `ctx.actions.register(...)` or -> `ctx.actions.registerFromCommand(...)` call — dual registration, exactly -> as `appos-plugin-ytdlp` ships it. +> **Manifest-declared ACTIONS are discoverable metadata, not executables.** +> The host replays `actions.definition` contributions into discovery at +> cold start and on plugin activation (visible in `ctx.actions.all()` / +> `palette.query()`, badged "manifest only"), but invoking one surfaces +> `ACTION_NOT_FOUND` until a runtime `ctx.actions.register(...)` or +> `ctx.actions.registerFromCommand(...)` call binds the handler. Pair +> EVERY contribution with its runtime registration — dual registration, +> exactly as `appos-plugin-ytdlp` ships it. ## Catalog bundle layout @@ -1134,13 +1136,10 @@ Notable properties: - **`remoteImage`** — `url` (file:// only in v1), `width`, `height`, `cornerRadius`, `maxDimension` (default 512) - **`textField`** — `placeholder`, `text` (initial contents), `action` - (fires on submit). Divergence: SDK 3.0.0 (`TextFieldDescriptor` and the - `textField()` builder) names the initial-contents property `text`, but - the shipped 1.0.0 host reads `value` — typed `text` compiles yet - renders empty today, while `value` fails excess-property checking. To - seed initial contents on today's host, include BOTH keys via an - assertion-cast properties object. + (fires on submit). The host reads `text` for the initial contents, + falling back to the legacy loose-JSON `value` key only for back-compat. + Write the typed `text` property (as `TextFieldDescriptor` and the + `textField()` builder do) — never add a `value` key. - **`progress`** — `value` (0–1, omit for indeterminate), `label`, `style` (`"bar"` | `"circular"`) - **`listItem`** — `title`, `subtitle`, `icon`, `iconColor`, `action`, @@ -1616,15 +1615,15 @@ export async function registerActions(ctx: PluginContext): Promise<() => Promise - `register` resolves to a handle token — keep it for `unregister` in your dispose path. -## 3. `extensions[]` + runtime dual registration (fn-163 workaround) +## 3. `extensions[]` + runtime dual registration **Files**: `plugin.json` + `src/main.ts` -Declare actions in the manifest so they are visible in catalogs and -manifest scans — AND bind the executable at runtime. Manifest-declared -actions currently never reach discovery on their own (host bug fn-163), so -ship BOTH, exactly as `appos-plugin-ytdlp` does. - +Declare actions in the manifest AND bind the executable at runtime. The +manifest entry is replayed into discovery (palette-visible, badged +"manifest only") but stays non-executable — `ACTION_NOT_FOUND` on +invoke — until the runtime registration binds the handler. Ship BOTH, +exactly as `appos-plugin-ytdlp` does. ```json { diff --git a/plugins/appos-dev/skills/appos-plugin-dev/SKILL.md b/plugins/appos-dev/skills/appos-plugin-dev/SKILL.md index f9de599..0051aa6 100644 --- a/plugins/appos-dev/skills/appos-plugin-dev/SKILL.md +++ b/plugins/appos-dev/skills/appos-plugin-dev/SKILL.md @@ -206,9 +206,10 @@ void token; // keep for ctx.actions.unregister(token) on dispose for an LLM reader. `registerFromCommand(commandId, metadata)` projects an existing command into the catalog. - Declare actions in the manifest `extensions[]` too, but ALWAYS pair with - the runtime registration — manifest-declared actions don't reach - discovery on their own yet (host bug fn-163; see - `reference/extension-api.md`). + the runtime registration — the manifest entry is replayed into discovery + (visible in `all()` / `palette.query()`, badged "manifest only") but + invoking it surfaces `ACTION_NOT_FOUND` until the runtime `register()` + binds the handler (see `reference/extension-api.md`). - Scopes: `actions.register` to register, `actions.invoke` to invoke (`actions.invoke.agent` additionally for agent-sourced invokes). @@ -425,7 +426,7 @@ load. Full pattern: `reference/patterns.md` §13 + §23. Two manifest families live in `reference/extension-api.md`: `extensions[]` (manifest-declarative core-plugin contributions — qualified-id grammar, -per-EP payloads, fn-163 dual-registration caveat for actions) and +per-EP payloads, dual-registration contract for actions) and `dependencies` (system binaries + plugin deps; full example: `reference/patterns.md` §23). diff --git a/plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md b/plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md index 89514df..88282a1 100644 --- a/plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md +++ b/plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md @@ -154,22 +154,21 @@ Other methods: |---|---|---| | `registerFromCommand(commandId, metadata)` | Projects an existing `ctx.commands` command into the action catalog | `actions.register` | | `invoke(id, input, source?)` | Invokes through the full pipeline; resolves to an `ActionReceipt` | `actions.invoke` (+ `actions.invoke.agent` for source `"agent"`) | -| `all()` | Lists the action catalog (runtime-registered only on the shipped host — see below) | `actions.list` | +| `all()` | Lists the merged action catalog (runtime-registered + manifest-declared — see below) | `actions.list` | | `unregister(handleToken)` | Removes an executable registration | `actions.register` | -By design `all()` also returns **manifest-only** entries (declared via -`extensions[]` but with no executable handler bound yet; invoking one -surfaces `ACTION_NOT_FOUND` until `register()` binds the handler). On the -shipped host this does NOT happen — manifest `actions.definition` -contributions never reach discovery (host bug fn-163; see the caveat -under "`extensions[]`" below), so `all()` and `palette.query()` list -runtime-registered actions only. Never write code that expects to find an -unbound manifest action in `all()`. +`all()` also returns **manifest-only** entries — `actions.definition` +contributions are replayed into discovery at cold start and on plugin +activation, so they surface in `all()` and `palette.query()` (badged +"manifest only" in the Action Browser) before any runtime call. They are +metadata, not executables: invoking one surfaces `ACTION_NOT_FOUND` until +`register()` / `registerFromCommand()` binds the handler (see +"`extensions[]`" below). #### `ctx.palette` — palette integration (fn-89) -`query(text, scope?)` searches the action catalog (the fn-163 caveat -above applies — runtime-registered actions only); `pin(id)` / +`query(text, scope?)` searches the merged action catalog (manifest-only +entries included — see `all()` above); `pin(id)` / `unpin(id)` manage palette pins (`palette.contribute.scope`); `history(limit?)` returns recent invocations (`palette.history`). @@ -422,12 +421,14 @@ conventionally under `contribution`: AppOS repo — its manifest carries recipe + sequence + trigger contributions and its `activate` is a no-op. -> **Caveat — manifest-declared ACTIONS don't reach discovery yet (host bug -> fn-163).** An `actions.definition` contribution alone currently never -> becomes palette-visible or invokable. Pair EVERY `actions.definition` -> contribution with a runtime `ctx.actions.register(...)` or -> `ctx.actions.registerFromCommand(...)` call — dual registration, exactly -> as `appos-plugin-ytdlp` ships it. +> **Manifest-declared ACTIONS are discoverable metadata, not executables.** +> The host replays `actions.definition` contributions into discovery at +> cold start and on plugin activation (visible in `ctx.actions.all()` / +> `palette.query()`, badged "manifest only"), but invoking one surfaces +> `ACTION_NOT_FOUND` until a runtime `ctx.actions.register(...)` or +> `ctx.actions.registerFromCommand(...)` call binds the handler. Pair +> EVERY contribution with its runtime registration — dual registration, +> exactly as `appos-plugin-ytdlp` ships it. ## Catalog bundle layout @@ -577,13 +578,10 @@ Notable properties: - **`remoteImage`** — `url` (file:// only in v1), `width`, `height`, `cornerRadius`, `maxDimension` (default 512) - **`textField`** — `placeholder`, `text` (initial contents), `action` - (fires on submit). Divergence: SDK 3.0.0 (`TextFieldDescriptor` and the - `textField()` builder) names the initial-contents property `text`, but - the shipped 1.0.0 host reads `value` — typed `text` compiles yet - renders empty today, while `value` fails excess-property checking. To - seed initial contents on today's host, include BOTH keys via an - assertion-cast properties object. + (fires on submit). The host reads `text` for the initial contents, + falling back to the legacy loose-JSON `value` key only for back-compat. + Write the typed `text` property (as `TextFieldDescriptor` and the + `textField()` builder do) — never add a `value` key. - **`progress`** — `value` (0–1, omit for indeterminate), `label`, `style` (`"bar"` | `"circular"`) - **`listItem`** — `title`, `subtitle`, `icon`, `iconColor`, `action`, diff --git a/plugins/appos-dev/skills/appos-plugin-dev/reference/patterns.md b/plugins/appos-dev/skills/appos-plugin-dev/reference/patterns.md index 48e3ab0..3103a7c 100644 --- a/plugins/appos-dev/skills/appos-plugin-dev/reference/patterns.md +++ b/plugins/appos-dev/skills/appos-plugin-dev/reference/patterns.md @@ -139,15 +139,15 @@ export async function registerActions(ctx: PluginContext): Promise<() => Promise - `register` resolves to a handle token — keep it for `unregister` in your dispose path. -## 3. `extensions[]` + runtime dual registration (fn-163 workaround) +## 3. `extensions[]` + runtime dual registration **Files**: `plugin.json` + `src/main.ts` -Declare actions in the manifest so they are visible in catalogs and -manifest scans — AND bind the executable at runtime. Manifest-declared -actions currently never reach discovery on their own (host bug fn-163), so -ship BOTH, exactly as `appos-plugin-ytdlp` does. - +Declare actions in the manifest AND bind the executable at runtime. The +manifest entry is replayed into discovery (palette-visible, badged +"manifest only") but stays non-executable — `ACTION_NOT_FOUND` on +invoke — until the runtime registration binds the handler. Ship BOTH, +exactly as `appos-plugin-ytdlp` does. ```json { diff --git a/plugins/appos-dev/skills/viewdescriptor-authoring/SKILL.md b/plugins/appos-dev/skills/viewdescriptor-authoring/SKILL.md index 04232e6..3eac615 100644 --- a/plugins/appos-dev/skills/viewdescriptor-authoring/SKILL.md +++ b/plugins/appos-dev/skills/viewdescriptor-authoring/SKILL.md @@ -41,7 +41,7 @@ Build native SwiftUI views for AppOS plugins using JSON ViewDescriptor trees. Ea |------|-----------|-------| | `button` | `title`, `action`, `tooltip`, `width` | With `width`: inline action button with hover. | | `listItem` | `title`, `subtitle`, `icon`, `iconColor`, `action`, `menuActions` | Primary row type. Trailing columns go in top-level `children`, NOT in `properties`. | -| `textField` | `placeholder`, `text`, `action` | Editable text input. Action fires on submit. SDK 3.0.0 types name the initial contents `text`, but the 1.0.0 host reads `value` — include both (assertion-cast) to seed initial text on today's host. | +| `textField` | `placeholder`, `text`, `action` | Editable text input. Action fires on submit. `text` seeds the initial contents (the host falls back to a legacy `value` key for back-compat only — do not write it). | | `progress` | `value` (0.0-1.0), `label`, `style` ("bar"/"circular") | Omit `value` for indeterminate. Default style: "bar". | | `section` | `title`, `icon`, `badge`, `isExpanded`, `id` | Collapsible group. Content goes in top-level `children`. |