docs: document v2.0 manifest contract#370
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Documents the AppKit plugin manifest v2.0 contract and how it propagates into the synced template manifest used by databricks apps init, giving plugin authors a single reference for resources, discovery, and post-scaffold guidance.
Changes:
- Added a new “Plugin manifest” reference page covering v2.0 fields, resource permissions, discovery descriptors, dependency ordering, and post-scaffold steps.
- Updated the templates documentation to describe v2.0 synced-manifest additions (
origin,scaffolding,postScaffoldpropagation). - Updated the custom plugins guide to recommend importing
manifest.jsonand attaching it viastatic manifest(matching core plugin patterns).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| docs/docs/plugins/manifest.md | New v2.0 plugin manifest contract reference (resources, discovery, dependencies, post-scaffold, optional metadata). |
| docs/docs/plugins/custom-plugins.md | Updates basic example to use JSON-authored manifests imported into the plugin class. |
| docs/docs/development/templates.md | Documents v2.0 additions to the synced template manifest (origin, scaffolding, and postScaffold propagation). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add docs/docs/plugins/manifest.md covering the v2.0 plugin manifest: resources, kind/cli discovery descriptors, field dependencies, transient prompts via `parents`, and plugin-level scaffolding rules. Update templates.md to cover the v2.0 template manifest: computed `origin` field, scaffolding descriptor + rules with substitutability gate, and scaffolding.rules propagation from plugin manifests. Update custom-plugins.md to recommend the manifest.json import pattern matching the core plugins. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu>
fb487ec to
f480521
Compare
Per Copilot review on #370: the published template-plugins.schema.json marks only `version` and `plugins` as top-level required. `scaffolding` is enforced via Zod superRefine (conditional on version=2.0), which JSON Schema cannot express. Reword both claims so readers validating against the published schema alone are not misled. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu>
MarioCadenas
approved these changes
May 21, 2026
Collaborator
MarioCadenas
left a comment
There was a problem hiding this comment.
LGTM! Please take a look at the comments and address them before merging 🙏🏻
scaffolding.flags in templates.md still showed the pre-curation 3-flag placeholder set (--template-dir, --config-dir, --profile). Replace with three representative real flags plus a reference table covering all 10 canonical flags shipped by TEMPLATE_SCAFFOLDING. lakebase scaffolding rules example in manifest.md still used a `must` with Drizzle-specific wording, but the merged lakebase manifest.json has no `must` and uses generic ORM language under `should`. Sync the JSON example and the substitutability-gate prose example to match. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu>
atilafassina
added a commit
that referenced
this pull request
May 22, 2026
* feat: extend plugin and template manifest schemas with discovery, postScaffold, and scaffolding Xavier loop: iteration 1 — Phase 1 (Schema Definitions & Type Generation) Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: add origin computation, scaffolding descriptor, and v2.0 template manifest emission Xavier loop: iteration 2 — Phase 2 (Origin Computation & Sync Enrichment) Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: add semantic validation for dependsOn cycles, discovery profile, and postScaffold Xavier loop: iteration 3 — Phase 3 (Semantic Validation) Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: annotate core plugin manifests with discovery descriptors and postScaffold steps Xavier loop: iteration 4 — Phase 4 (Core Plugin Manifest Annotations) Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: inline template schema resourceFieldEntry and resourceRequirement for origin support JSON Schema Draft-07 additionalProperties:false blocks allOf composition. Inlined both defs in template schema so origin validates correctly. Xavier loop: iteration 5 — Phase 5 (Integration & Backpressure) Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * chore: address code reviews * chore: untrack .claude/scheduled_tasks.lock Accidentally committed in a1c30e3; it's ephemeral Claude Code loop state, not source. Flagged in PR #261 review. Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: introduce zod-canonical manifest schema (phase 1) Add Zod schemas mirroring the existing plugin and template manifest JSON Schemas, the @standard-schema/spec dep that consumer code will use in phase 2, and a Zod→JSON Schema generator wired into build:package and generate:types. AJV continues to run in validate-manifest.ts; this is purely additive groundwork. Parity test uses fixture equivalence (Strategy B) — Zod 4's toJSONSchema emits per-type permission constraints as oneOf-of-discriminated-variants while the hand-written schema uses allOf+if/then over $defs/$ref, so byte parity is structurally infeasible. The test asserts AJV-with-legacy and Zod-with-new return matching accept/reject verdicts on the four core plugin manifests plus 5 synthetic plugin and 3 synthetic template fixtures (12 cases total). Build-pipeline byproducts of running pnpm build && pnpm docs:build cleanly are also captured: docs/static/schemas/plugin-manifest.schema.json loses a description field that copy-schemas.ts overwrote from the package-internal source (where the description was never present), and template/appkit.plugins.json gains origin enrichment on jobs.id and serving.name fields the parent PRD's enrichFieldsWithOrigin pass missed. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * refactor: switch validation runtime to standard schema (phase 2) Replace AJV with Zod-via-Standard-Schema in validate-manifest.ts. The CLI validator now calls `~standard.validate` against the Zod schemas authored in phase 1; consumer code never imports zod directly. Cycle detection, dangling-reference checks, and the <PROFILE> placeholder constraint move from the standalone runSemanticValidation pass into Zod refinements co-located with the shape: - resourceRequirementSchema gains a superRefine running DFS over discovery.dependsOn — dangling refs emit at fields.<name>.discovery.dependsOn, cycles emit at the resource root with the existing 'a → b → c → a' chain. - discoveryDescriptorSchema.refine() enforces the <PROFILE> placeholder on cliCommand. - postScaffoldStepSchema.instruction tightens to z.string().min(1). origin-drift detection (validateDiscoveryOrigin) is dropped — origin becomes a transform in phase 3, eliminating the desync surface entirely. validate-manifest.ts shrinks from 498 to 177 lines: loadSchema, getPluginValidator, getTemplateValidator, the AJV compile cache, the JSON-pointer humanizer, the AJV error formatter, runSemanticValidation, validateDependsOn, validateDiscoveryProfile, validateDiscoveryOrigin, validatePostScaffold, and formatSemanticIssues all delete. Tests rewrite to drive validateManifest end-to-end and assert on the resulting SemanticIssue shape. validateManifest returns the original input object as `manifest` rather than result.output — Zod parsing is used purely as a verifier here so property order is preserved for round-trip writers like add-resource. Phase 3 will introduce the first real transform (origin), at which point output-vs-input distinction becomes intentional. ajv and ajv-formats remain in packages/shared/package.json for the phase-1 parity test (deletes in phase 5). Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * refactor: derive origin via zod transform (phase 3) templateFieldEntrySchema gains a .transform() that computes origin from localOnly/value/resolve and emits it on every parse. origin is accepted as optional input but always overwritten — drift-by-construction is now structurally impossible. The new sync.test.ts case verifies this: a field with value: "5432" and stale input origin: "user" parses to "static". enrichFieldsWithOrigin and its mutation pass delete from sync.ts. The template manifest is now built by parsing each field through templateFieldEntrySchema before serialization. Per-field parse (rather than whole-manifest parse) is chosen because Zod 4's strict-object parse reorders keys aggressively, churning resource and plugin entries; per-field parse leaves the surrounding structure in input order. Sync output is byte-identical to phase 2 (md5 verified). computeOrigin and Origin type delete from manifest-types.ts and sync.ts. The replacement, computeOriginFromField, is private to manifest.ts and invoked only by the transform — nothing else in the codebase needs origin computation now that validateDiscoveryOrigin (deleted in phase 2) is gone. generate-json-schema.ts passes io: "input" to z.toJSONSchema so the transform doesn't break schema emission. The published JSON Schema describes what plugin authors write (no origin slot), not the transformed output — exactly the right semantic for IDE intellisense and external validators. template/appkit.plugins.json regenerated by build pipeline (pre-existing drift, not introduced here). Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: reshape discovery as discriminated union (phase 4) Replace the free-form discoveryDescriptorSchema with a discriminated union on `type`: - kind variant: { type: "kind", resourceKind, select?, display?, dependsOn?, shortcut? } resourceKind enum is the closed set of databricks resources AppKit owns command templates for: warehouse, genie_space, postgres_branch, postgres_database, volume. - cli variant: { type: "cli", cliCommand, selectField, displayField?, dependsOn?, shortcut? } preserves the existing free-form shape as an escape hatch for bespoke resources not yet in the kind enum. The <PROFILE> placeholder .refine() moves from the top-level descriptor to the cli variant only — kind has no cliCommand to validate. The cycle and dangling-reference DFS on resourceRequirementSchema reads field.discovery?.dependsOn generically and continues to work across both variants. A typed RESOURCE_KIND_COMMANDS map ships next to the schema. Each entry declares the CLI command template (with <PROFILE> placeholder + optional {<fieldName>} placeholders for dependsOn substitution) and an optional unwrap path for wrapped responses. Volume's catalog/schema parent context is documented in a code comment as a phase 6 MUST-rule concern, not a schema construct. The four core plugin manifests migrate to the kind variant: - analytics: { type: "kind", resourceKind: "warehouse" } - genie: { type: "kind", resourceKind: "genie_space" } - lakebase: branch → postgres_branch, database → postgres_database (dependsOn: "branch") - files: { type: "kind", resourceKind: "volume", select: "full_name" } Lakebase carries select: "name" (non-default for postgres_branch and postgres_database); files carries select: "full_name". Defaults are kind- specific identifiers and live in the command map / runner (out of scope). The Zod-derived ResourceRequirement is a discriminated union (per-type permission tightness baked in). Two consumer interface declarations in packages/shared/src/plugin.ts and packages/appkit/src/registry/types.ts previously did `interface ResourceRequirement extends GeneratedResourceRequirement` — TS interfaces cannot extend union types. Both flatten to structural interfaces with `permission: string`. This matches the previous consumer-facing shape (legacy generated permission was already loose string); per-variant tightness is enforced at schema parse time, where it belongs. add-resource.ts's literal entry construction casts to ResourceRequirement for the same reason. manifest-types.ts re-export source switches from the legacy plugin-manifest.generated to the canonical Zod schemas/manifest. The generated.ts file is now orphaned (no source imports it) and deletes in phase 5. The phase 1 parity test (json-schema-parity.test.ts) deletes — it asserted AJV-with-legacy-schema and Zod-with-new-schema return matching verdicts on the four core plugin manifests, but those manifests now use type: "kind" which the legacy schema doesn't understand. The test was the phase-1 transition gate; once the contract intentionally diverges it loses meaning. template/appkit.plugins.json and docs/docs/api/appkit/* re-emitted by the build pipeline. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * chore: delete legacy json-schema pipeline (phase 5) Now that Zod is canonical and the validation runtime calls ~standard.validate, the legacy artifacts have nothing left to do. This phase deletes them and the build steps that produced them. Deleted: - packages/shared/src/schemas/plugin-manifest.generated.ts (orphaned since phase 4 switched manifest-types.ts re-exports to Zod). - packages/shared/src/schemas/plugin-manifest.schema.json, template-plugins.schema.json (legacy hand-written JSON Schema; Zod is now the source, JSON Schema is generated into docs/static/schemas by tools/generate-json-schema.ts). - tools/generate-schema-types.ts (JSON Schema → TS codegen, replaced by tools/generate-json-schema.ts going the other way). - docs/scripts/copy-schemas.ts (copied the legacy schemas to docs/static, now no-op). Dependencies removed from packages/shared: - ajv, ajv-formats — runtime validator gone in phase 2. - json-schema-to-typescript — codegen tool gone above. Build pipeline updated: - root package.json generate:types and packages/shared build:package scripts drop generate-schema-types.ts. - packages/shared/tsdown.config.ts drops the copy: block (the .json files no longer exist). - docs/package.json drops the copy-schemas script and removes it from the gen chain. - knip.json drops json-schema-to-typescript from ignoreDependencies. - .github/workflows/ci.yml `Check generated types are up to date` step drops the deleted plugin-manifest.generated.ts and adds docs/static/schemas/*.schema.json (now owned by generate-json-schema.ts). Source migrations to Zod: - schema-resources.ts: was reading plugin-manifest.schema.json at runtime to derive resource type options and per-type permissions. Now imports the per-type permission schemas and resourceTypeSchema from the Zod module and reads .options. No filesystem reads, no caching, no defensive null branches — values are module-level constants now. Public API preserved. - tools/generate-registry-types.ts: hidden consumer that also read the legacy JSON schema. Same migration. - packages/shared/src/cli/commands/plugin/manifest-types.ts: shrunk to a thin re-export shim of z.infer types and StandardSchemaV1. Type-level fix: - TemplatePlugin / TemplateResourceRequirement / TemplateFieldEntry / TemplatePluginsManifest type aliases switched to z.input instead of z.infer/z.output. The field-level origin transform makes origin REQUIRED on z.output, but consumer code (sync.ts) constructs template plugins without origin before writeManifest runs the transform at write time. z.input gives the pre-transform shape, matching the runtime invariant. Stale JSDoc references to GeneratedPluginManifest and plugin-manifest.generated.ts updated. The published JSON Schema URL is unchanged. Plugin authors' VSCode intellisense continues to work; the docs/static/schemas/*.json files are now byte-stable across runs (generated solely by the Zod-fed generate-json-schema.ts) and contain the new discriminated-union discovery shape. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: harden scaffolding directives (phase 6) Final phase of the manifest zod refactor. Three small, targeted changes to the scaffolding descriptor: 1. Rule items are constrained to ≤ 120 chars via z.string().max(120) on both rules.never[] and rules.must[]. The schema can't validate prose content but it can stop a directive from growing into a paragraph — enforces the "short directive" intent at the only boundary the schema can express. 2. TEMPLATE_SCAFFOLDING moves from sync.ts into the schema module. The constant lives next to the schemas it conforms to, with a `satisfies z.infer<typeof scaffoldingDescriptorSchema>` clause for compile-time validation against the input shape. sync.ts imports it. 3. New MUST rule directive describing volume parent-context handling: "When discovering volume resources, prompt the user for catalog and schema before listing volumes." The kind variant for `volume` doesn't model catalog/schema parents in the schema (per PRD design decision #7 — hierarchical context as MUST rule, not schema structure); this directive carries the requirement to LLM scaffolding agents instead. Tests added under "scaffolding rule item maxLength (Phase 6)": - never[]/must[] items exceeding 120 chars produce errors with the right path and message. - 120 chars exactly is accepted (≤ semantics). - A mixed-length array flags only the offending entry. - TEMPLATE_SCAFFOLDING parses cleanly against scaffoldingDescriptorSchema. - The synced template manifest carries the new volume MUST rule string. template/appkit.plugins.json regenerated by sync:template — the new rule string is now in scaffolding.rules.must. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: dedupe zod resolution for json-schema generator CI's `Check generated types are up to date` step was failing because two zod versions live in the workspace: - 4.1.13 hoisted at root (transitive via clean-app's eslint-plugin-react-hooks → zod-validation-error peer) - 4.3.6 in packages/shared (explicit dep) `tools/generate-json-schema.ts` imports zod from its own location, which resolves to root's 4.1.13. `packages/shared/src/schemas/manifest.ts` imports zod, resolving to shared's 4.3.6. The two runtimes operate on each other's schema objects, and the older zod's `toJSONSchema` doesn't extract all the constraints (pattern, minLength, propertyNames) that the newer zod baked into the schemas. CI's pnpm install resolves them consistently and emits the richer output, which then drifts from what's committed. Adding zod@4.3.6 as a root devDependency makes pnpm hoist the matching version to the top-level node_modules. The generator now resolves the same zod runtime as the schema module, and the JSON Schema output is byte-stable across local and CI. Regenerated docs/static/schemas/*.schema.json carry the now-emitted constraints (~135 minLength/pattern entries on plugin-manifest, similar on template-plugins). The constraints were always in the Zod schemas since phase 1 — they just weren't surviving the cross-version emit. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: address review findings — cli command syntax + lakebase parent + strict config Three real bugs flagged by the multi-model review of PR #261, fixed in this iteration. (CRITICAL cliCommand RCE hardening + HIGH z.lazy() perf deferred to follow-up PRs.) 1. RESOURCE_KIND_COMMANDS strings now match the real Databricks CLI (verified against v0.299.0 --help output): - `genie list` → `genie list-spaces` - `volumes list <catalog>.<schema>` → `volumes list {catalog} {schema}` (two separate positionals, prompted via the volume MUST rule) - `postgres list-branches` → `postgres list-branches {project}` with a project parent (covered by Fix 2 below) 2. Lakebase branch discovery is now actually runnable: - resourceKindSchema gains `postgres_project`. RESOURCE_KIND_COMMANDS gains the corresponding `databricks postgres list-projects` entry. - lakebase/manifest.json gains a new `project` field with `discovery: { type: "kind", resourceKind: "postgres_project", select: "name" }`. - The existing `branch` field's discovery adds `dependsOn: "project"`, so the parent project name flows into the branch listing command. 3. configSchemaPropertySchema and configSchemaSchema gain `.strict()`, so plugin config-schema typos no longer pass validation silently. `additionalProperties` (a standard JSON Schema keyword used by three core plugins — serving, vector-search, genie — inside nested property entries) is added explicitly as `z.union([z.boolean(), configSchemaPropertySchema]).optional()` so those manifests keep validating; this is a deliberate canonical addition, not a loosening of strict mode. Auto-regenerated by the build pipeline: - docs/static/schemas/{plugin-manifest,template-plugins}.schema.json - template/appkit.plugins.json Backpressure: typecheck=0, test=0 (108 files / 2136 tests), build=0, docs:build=0, knip=0, check:fix=0. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * chore: mark auto-generated artifacts as linguist-generated GitHub collapses these in the diff view by default and excludes them from language stats. The files are emitted by the build pipeline and should not need reviewer attention. - docs/static/schemas/*.schema.json — emitted by tools/generate-json-schema.ts - template/appkit.plugins.json — emitted by pnpm sync:template - packages/appkit/src/registry/types.generated.ts — generate-registry-types.ts - packages/appkit/src/plugins/*-exports.generated.ts — generate-plugin-entries.ts - docs/docs/api/** — typedoc API reference - pnpm-lock.yaml — pnpm Reduces perceived PR size on this branch by ~10k lines (two regenerated JSON Schema files alone account for ~91% of insertions on PR #261). Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: deny shell metacharacters on cli discovery descriptor The `cli` variant of discoveryDescriptor accepts a free-form Databricks CLI command supplied by the plugin author. With no further constraint beyond the existing `<PROFILE>` placeholder check, the shape is open to two unrelated foot-guns reviewers flagged: - output-shape brittleness: a plugin writes `selectField: ".id"` but the CLI returns a wrapped object (e.g. `{warehouses: [...]}`); jq fails silently at scaffold time - shell-injection-if-executed: when an executor lands and passes the string to a shell, statement separators / pipes / command substitution / redirects all become attack surface The `kind` variant addresses both for first-party plugins (AppKit owns the command and unwrap rules). The `cli` variant is the escape hatch for third-party plugins that need bespoke commands. Tighten it cheaply now, before anyone ships against the loose shape: - new SHELL_METACHAR_RE blocks `;`, `|`, `&`, backtick, `$`, newlines on both `cliCommand` and `shortcut`. Angle brackets are still permitted so `<PROFILE>` (and future `<…>` placeholders) work. - describes on cliCommand and the variant overall direct authors to use `kind` for first-party resources and call out that the cli shape is intentionally minimal and may tighten further. Not a security boundary on its own — executors must still spawn(argv) not shell-exec the string. argv-array form, denylist of shell operators in argv, and an output-shape contract are all separate decisions tied to the executor PR. Two new test cases cover the two refinements (cliCommand + shortcut). Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: cap postScaffold instruction at 200 chars postScaffoldStepSchema.instruction was bounded below (.min(1)) but had no upper bound, while scaffolding.rules.must[] / never[] are capped at 120 chars per phase 6. Same intent applies to postScaffold instructions — they are checklist items, not prose — so add .max(200) with a parse-time error message. 200 (vs 120 for rules.must/never) allows short imperative sentences with placeholders; the longest existing core-plugin instruction is ~120 chars, so all current instructions fit with headroom. Regenerated docs/static/schemas/*.schema.json carry the new maxLength entry on the postScaffold step's instruction field. Two boundary tests added next to the existing "rejects empty postScaffold instruction" test (rejects 201, accepts exactly 200). Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: replace postScaffold with plugin-level scaffolding.rules Delete postScaffold from canonical Zod schema and add plugin-level scaffolding.rules ({must?, should?, never?}, ≤120 chars each) gated by the substitutability principle. Adds parity should[] on template-level scaffoldingRules. Migrates analytics, files, genie, lakebase manifests off postScaffold per the A5 mapping table. xavier loop iteration 1 — phase 1 of plugin-manifest-refactor amendment. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: prune TEMPLATE_SCAFFOLDING.rules.must per substitutability gate Apply the A4 audit from the 2026-05-18 manifest amendment. All 4 prior must[] entries are substitutable (skill content, derivable from requiredByTemplate, derivable from field.env, or belongs on volume discovery descriptor — A6 candidate for a later phase). Net result: must=[], should=[] (parity), never keeps the 3 cross-cutting guardrails. Regression test pins the shape. xavier loop iteration 2 — phase 2 of plugin-manifest-refactor amendment. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * feat: model kind discovery parents for transient query inputs Extend RESOURCE_KIND_COMMANDS value shape with parents?: readonly string[]. parents declares free-text prompts the runner must collect before invoking a kind's listing command — each entry substitutes the matching {name} placeholder in the command template. Unlike dependsOn (which references a sibling field), parents covers transient inputs not persisted as fields. Applies to volume.parents = ['catalog', 'schema'], replacing the prose rule deleted in the A4 audit. Regression tests pin volume's parents shape and confirm no other kind currently uses parents. A6 decisions (b) and (c) accept-as-prose — see ~/.xavier/tasks/plugin-manifest-refactor.md. xavier loop iteration 3 — phase 3 of plugin-manifest-refactor amendment. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * chore(.claude): extend plugin commands with manifest v2.0 semantic checks audit-core-plugin: Step 3.6 — substitutability-gate patterns (permission duplication, existence tautology, inactionable --set, enum-or, length cap); discovery descriptor completeness; RESOURCE_KIND_COMMANDS.parents vs dependsOn consistency. All findings feed Category 1 (Manifest Design). review-core-plugin: Step 5.6 — same gate/discovery checks scoped to changed manifest files only. create-core-plugin: Step 4e.1 — post-scaffold enrichment pass that adds discovery descriptors to user-supplied fields and gates scaffolding.rules against the substitutability principle with 5 reject patterns + 3 legitimate categories. No changes to plugin-best-practices.md or plugin-review-guidance.md reference docs — additions are procedural workflow steps, not narrative rules. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * refactor: sharpen template scaffolding rules for skill conflict detection The original three never entries surfaced false-positive conflicts with plugin setup instructions during integration testing against the databricks-apps skill (Track B PR databricks/databricks-agent-skills#79): - 'Modify files inside the template directory' caught every plugin must rule that edits scaffolded files (genie spaces config, lakebase migrations, analytics queries). The rule was either unreachable (if read as the SDK source-of-truth) or contradictory (if read as the scaffolded output, which the user owns post-init). Deleted. - 'Hardcode workspace-specific values in template files' conflated workspace IDs (which are correctly committed to bundle config) with credentials (which must not be). Replaced with a must/never pair that names the legitimate destinations (app.yaml, databricks.yml, .env) and the leak path (client bundle). - 'Skip resource configuration prompts' conflicted with the --set non-interactive path. Replaced with a should/never pair covering the actual decision-time failures: ask the user when uncertain; never guess when discovery returns zero or multiple options. Net: 0 must + 0 should + 3 never -> 1 must + 1 should + 2 never. All entries describe agent behaviors at decision points (substitutability gate passes), each under 120 chars, and the merged set is precedence- and phase-clean per the skill PR's protocol. Regression test in validate-manifest.test.ts updated to pin the new contents. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * chore: address review comments (#4, #5, #9) - tools/generate-json-schema.ts, tools/generate-registry-types.ts: drop .ts extensions on relative imports — editors with TS LS configured without allowImportingTsExtensions flagged them as errors (per MarioCadenas on PR #261). tsx resolver accepts both forms; the rest of tools/ already imports without extensions. - lakebase/manifest.json: reword the migrations must rule. Previous text ('pnpm drizzle:migrate') referenced a script that doesn't exist in template/package.json AND a package manager (pnpm) the template doesn't use (template scripts use npm throughout). New text is ORM-agnostic: 'After init, run any database migrations for your chosen ORM before first request'. Preserves the don't-forget-migrations directive without presuming a tool the template doesn't ship. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: restore inherited-field descriptions on PluginManifest interface Phase 5 deleted plugin-manifest.generated.ts (the json-schema-to-typescript output that carried JSDoc converted from Zod .describe() strings). The Omit<GeneratedPluginManifest, ...>-based PluginManifest interface in packages/shared/src/plugin.ts then inherited fields from z.infer<typeof pluginManifestSchema> — a computed type with no JSDoc to render. TypeDoc walks the TS surface (not the JSON Schema), so 10 inherited field descriptions silently vanished from docs/docs/api/appkit/Interface.PluginManifest.md. Redeclare the affected fields locally on PluginManifest with JSDoc copied verbatim from the Zod .describe() text. Restores the rendered descriptions in TypeDoc output. Drift risk acknowledged: if Zod .describe() changes, the JSDoc here stays stale until manually synced. A proper follow-up would either (a) re-emit JSDoc'd .d.ts from the regenerated JSON Schema as a codegen artifact pointed at by TypeDoc, or (b) adopt a Zod-aware TypeDoc plugin. Both out of scope for this PR. Addresses PR #261 review comment from MarioCadenas on docs/docs/api/appkit/Interface.PluginManifest.md. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: collapse restored JSDoc to single line for diff-clean docs regen Multi-line JSDoc comments produced multi-line description text in the TypeDoc-rendered markdown, which git diffed against the original single-line descriptions in main. Functionally identical; just whitespace. Collapsing onSetupMessage, hidden, and stability JSDoc to single-line matches the original markdown formatting verbatim. Net change vs main on docs/docs/api/appkit/Interface.PluginManifest.md is now just the See link swap (deleted plugin-manifest.generated.ts -> Zod source of truth pointer). Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: align TEMPLATE_SCAFFOLDING flag required to false; resync template CI sync:template diff check caught two source/template drifts on 4ae1741: 1. lakebase manifest had been moved must -> should in the source, but the committed template still carried the rule under must. Resyncing now that sync:template is invoked as part of pnpm build. 2. TEMPLATE_SCAFFOLDING declared --template-dir and --config-dir as required: true, but the committed template had them as required: false (per MarioCadenas on PR #261 #6 — these flags aren't actually required by databricks apps init). Flipping the source constant to match; resync now produces consistent output. After this commit, source and template are in lockstep; CI sync:template diff check should pass. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * chore: scrub PRD/phase leakage from source comments Code comments should describe implementation details, not project-management state. Stripped references to phase numbers (Phase 1-6), the substitutability gate amendment, A4/A5/A6 audit candidates, and Track B PR cross-links from: - packages/shared/src/schemas/manifest.ts (header comment) - packages/shared/src/plugin.ts (re-export comment) - packages/shared/src/cli/commands/plugin/manifest-types.ts (shim header) - packages/shared/src/cli/commands/plugin/schema-resources.ts (header) - packages/shared/src/cli/commands/plugin/validate/validate-manifest.test.ts (describe/it names + inline comments) - tools/generate-json-schema.ts (header — dropped the phase narrative entirely) - tools/generate-registry-types.ts (header) Where the original prose carried an implementation fact worth keeping (transform overwrites input, parents replaces dependsOn for volume, strict object rejects postScaffold, etc.), the fact is preserved phase-free. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * fix: curate scaffolding.flags to match real CLI surface (#261) Replace stale --template-dir / --config-dir entries with the canonical databricks apps init flag set, derived from cmd/apps/init.go: --name (required, with kebab-case pattern), --template, --version, --features (with no-whitespace pattern), --set, --output-dir, --description, --run, --auto-approve, --profile Excluded by design: --branch (niche GitHub-template only, mutually exclusive with --version), --deploy (post-creation side effect to shared workspace), --warehouse-id (deprecated), --plugins (hidden alias for --features). Addresses PR feedback on template/appkit.plugins.json scaffolding.flags (thread r3282166233): --name is required by the scaffolder because {{.projectName}} populates package.json, databricks.yml, and .env. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * docs: document v2.0 manifest contract (#370) * docs: document v2.0 manifest contract Add docs/docs/plugins/manifest.md covering the v2.0 plugin manifest: resources, kind/cli discovery descriptors, field dependencies, transient prompts via `parents`, and plugin-level scaffolding rules. Update templates.md to cover the v2.0 template manifest: computed `origin` field, scaffolding descriptor + rules with substitutability gate, and scaffolding.rules propagation from plugin manifests. Update custom-plugins.md to recommend the manifest.json import pattern matching the core plugins. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * docs: clarify scaffolding requirement is CLI-enforced, not JSON-Schema Per Copilot review on #370: the published template-plugins.schema.json marks only `version` and `plugins` as top-level required. `scaffolding` is enforced via Zod superRefine (conditional on version=2.0), which JSON Schema cannot express. Reword both claims so readers validating against the published schema alone are not misled. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> * docs: align manifest v2.0 docs with merged PR #261 schema scaffolding.flags in templates.md still showed the pre-curation 3-flag placeholder set (--template-dir, --config-dir, --profile). Replace with three representative real flags plus a reference table covering all 10 canonical flags shipped by TEMPLATE_SCAFFOLDING. lakebase scaffolding rules example in manifest.md still used a `must` with Drizzle-specific wording, but the merged lakebase manifest.json has no `must` and uses generic ORM language under `should`. Sync the JSON example and the substitutability-gate prose example to match. Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu> --------- Signed-off-by: Atila Fassina <atila@fassina.eu> --------- Signed-off-by: Atila Fassina <atila@fassina.eu>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #261. Documents the v2.0 plugin manifest contract introduced there.
Summary
docs/docs/plugins/manifest.md— full v2.0 manifest reference: resources (per-type permissions),kind/clidiscovery descriptors,dependsOnfield ordering, post-scaffold steps, optional metadata.docs/docs/development/templates.md— covers the synced template manifest's v2.0 additions: computedoriginfield (with precedence rules),scaffoldingdescriptor (command + flags +never/mustrules),postScaffoldpropagation.docs/docs/plugins/custom-plugins.md— recommends theimport manifest from "./manifest.json"+static manifestpattern that all core plugins use, with a JSON-side example.No code changes — docs only. Stacked-PR base will flip to
mainonce #261 lands.Test plan
pnpm docs:build— Docusaurus production build passes, no broken links.pnpm check:fix— Biome clean.pnpm -r typecheck— clean across all packages (docs included).This pull request and its description were written by Isaac.