Skip to content

feat: new manifest format#261

Merged
atilafassina merged 33 commits into
mainfrom
manifest-ax
May 22, 2026
Merged

feat: new manifest format#261
atilafassina merged 33 commits into
mainfrom
manifest-ax

Conversation

@atilafassina
Copy link
Copy Markdown
Contributor

@atilafassina atilafassina commented Apr 8, 2026

Summary

Evolves the AppKit plugin manifest contract from v1.0 to v2.0 and swaps the canonical authoring surface from hand-written JSON Schema to Zod via the Standard Schema interface. AJV, the json-schema-to-typescript codegen pipeline, and the standalone semantic-validator delete; refinement and .transform() on Zod schemas replace them.

Mid-review (2026-05-18) the design picked up a substitutability-gate amendment that deleted postScaffold entirely in favor of a unified plugin-level scaffolding.rules block, and audited every prose rule against the gate. See "2026-05-18 amendment" below.

Important

~91% of insertions are auto-regenerated artifacts — two JSON Schema files (docs/static/schemas/{plugin-manifest,template-plugins}.schema.json) account for +10,400 lines.

.gitattributes marks all generated artifacts linguist-generated=true. GitHub collapses them in the file list — substantive review surface is ~32 files / ~2,300 insertions.

What changes vs main

Contract

  • discoveryDescriptor is a discriminated union: { type: "kind", resourceKind, ... } | { type: "cli", cliCommand, ... }. Six known kinds: warehouse, genie_space, volume, postgres_project, postgres_branch, postgres_database.
  • cli variant rejects shell metacharacters (;, |, &, backtick, $, newlines) on cliCommand and shortcut. .describe() notes direct authors to kind for first-party resources and flag the cli shape as minimal-and-may-tighten.
  • RESOURCE_KIND_COMMANDS map next to the schema; commands verified against Databricks CLI v0.299.0. volume kind carries parents: ["catalog", "schema"] — declares free-text prompts the runner must collect before invoking discovery (replaces the prior prose rule).
  • origin on template fields is a .transform() output (drift-impossible).
  • rules.must / rules.should / rules.never items capped at maxLength: 120. Per-bucket and cross-bucket dedup enforced via .superRefine().
  • 4 core plugin manifests (analytics, files, genie, lakebase) migrated to the kind variant; lakebase gains a project field.

Authoring surface

  • New: packages/shared/src/schemas/manifest.ts (Zod schemas + RESOURCE_KIND_COMMANDS + TEMPLATE_SCAFFOLDING).
  • New: tools/generate-json-schema.ts (Zod → draft-07 JSON Schema, emits to docs/static/schemas/).
  • New runtime dep on packages/shared: @standard-schema/spec.

Deletions

  • Deps removed: ajv, ajv-formats, json-schema-to-typescript.
  • packages/shared/src/schemas/{plugin-manifest.schema.json,template-plugins.schema.json,plugin-manifest.generated.ts}.
  • tools/generate-schema-types.ts, docs/scripts/copy-schemas.ts.
  • validate-manifest.ts (498 → 177 lines): AJV plumbing + runSemanticValidation + helpers.
  • enrichFieldsWithOrigin, validateDiscoveryOrigin.
  • postScaffoldStepSchema and postScaffold field (post-amendment — see below).

Migrated to Zod-direct

  • schema-resources.ts and tools/generate-registry-types.ts (no more runtime JSON-schema reads).
  • manifest-types.ts is now a re-export shim (z.infer types + StandardSchemaV1).

Plugin Rules (per core plugin, scaffolding.rules)

Plugin must should
analytics Before init, ensure the SQL Warehouse passed via --set analytics.sql-warehouse.id is running After init, ensure config/queries/ has at least one .sql file before running npm run typegen
files Before init, verify your Unity Catalog volume exists and you have WRITE_VOLUME permission
genie After init, configure the 'spaces' map in plugin config with alias-to-Space-ID mappings
jobs (no rules) (no rules)
lakebase After init, run database migrations via 'pnpm drizzle:migrate' before first request After init, verify Lakebase connectivity with 'psql $PGHOST -c "select 1"'

Template-level scaffolding.rules

{
  "must": [
    "Keep all secrets and credentials only in app.yaml, databricks.yml, and/or .env"
  ],
  "should": [
    "ask user when in doubt of resource to use for plugin"
  ],
  "never": [
    "guess resources when multiple or no options are available",
    "embed secrets in files that will go to the client-bundle"
  ]
}

Each entry passes the substitutability gate (describes a cross-cutting agent behavior at decision time, not derivable from structured manifest data). Each ≤120 chars. Conflict-detection-safe against the current plugin rule corpus (no mustnever overlaps).

2026-05-18 amendment — substitutability gate

The original v2.0 plan shipped postScaffold arrays ({ instruction, required }) at the plugin level. During v2.0 implementation we found the format couldn't participate in the databricks-apps agent skill's command-construction flow — prose doesn't compose across plugins, duplicates work the skill drives from structured resources, and { instruction, required } adds zero parseable signal.

The amendment introduces one design principle and applies it uniformly:

A prose rule in the manifest is legitimate only if it cannot be expressed as structured data on a resource, plugin config, or template.

Changes that landed under the amendment:

  • postScaffold deleted. postScaffoldStepSchema and the postScaffold field removed from both pluginManifestSchema and the template plugin schema. All four core plugins migrated to plugin-level scaffolding.rules.
  • Plugin-level scaffolding.rules with must / should / never arrays (≤120 chars each, no duplicates within a bucket, no overlap across buckets — all enforced via .superRefine()).
  • Template-level scaffoldingRulesSchema gains should[]? for parity with plugin-level.
  • TEMPLATE_SCAFFOLDING audit — original 4 must entries pruned (every one was substitutable: skill content, derivable from requiredByTemplate, derivable from field.env, or a discovery-descriptor concern). Original 3 never entries replaced with 2 sharper rules covering credentials hygiene and resource-selection decision points; one entry deleted as unreachable. See "Template-level scaffolding.rules" above.
  • A6 schema-extension decisions:
    • (a) Volume catalog+schema prerequisite → EXTEND: RESOURCE_KIND_COMMANDS.volume.parents = ["catalog", "schema"]. Models transient query inputs as a kind-level property without conflating with dependsOn (which references sibling fields).
    • (b) Config-driven --set derivation for genie spaces → ACCEPT AS PROSE pending Track B confirmation of the skill's consumption model.
    • (c) Liveness preconditions (analytics warehouse) → ACCEPT AS PROSE. Runtime state checks aren't structural data.
  • Per-plugin rule audit: 7 substitutability-gate violations introduced during experimentation were caught and reverted (permission duplications, resource-existence tautologies, inactionable-at-scaffold-time directives). Net effect: zero new plugin rules survive the gate that weren't already in the post-Phase-1 baseline.

.claude/commands/ skill extensions

Outside the packages/ tree but relevant to reviewers — the in-repo plugin commands gained procedural v2.0 semantic checks:

  • audit-core-plugin.md — Step 3.6 (3.6a substitutability-gate patterns, 3.6b discovery descriptor completeness, 3.6c parents vs dependsOn consistency). All findings feed Category 1 (Manifest Design).
  • review-core-plugin.md — Step 5.6 with the same checks scoped to changed manifest files only.
  • create-core-plugin.md — Step 4e.1 post-scaffold enrichment that walks each user-supplied field to add a discovery block and gates any scaffolding.rules against 5 explicit reject patterns.

No changes to plugin-best-practices.md or plugin-review-guidance.md — the additions are procedural workflow steps, not narrative rules.

Track B — consumer skill (cross-repo)

The databricks-apps skill in databricks/databricks-agent-skills#79 ("docs(apps): update AppKit scaffolding setup for Manifest 2.0") adds a new "Scaffolding Rules Protocol" subsection to skills/databricks-apps/SKILL.md (skill version bump 0.1.1 → 0.1.2). It consumes the rules emitted by this PR:

  1. Gather rules from selected --features plugins + every plugin with requiredByTemplate: true + template-level
  2. Precedence — manifest rules override skill-baked directives
  3. Phase orderingBefore init / After init prefixes
  4. Conflict detection — plugin must ↔ template never stops the flow
  5. Reporting — surface merged guardrails to the user before init

Sequencing: Track A (this PR) ships first. Track B is opened, not blocking merge — plugin-level rules are advisory metadata in the transition state. Failure mode if Track B lands later: degraded (third-party plugin guardrails don't fire), not broken (resources still drive the build).

End-to-end integration runbook lives in ~/.xavier/research/manifest-2-skill-integration-test.md (vault note, not in this repo).

Deferred to follow-up PRs

Change Note
TypeScript-authored manifests (defineManifest({ ... })) Future work; no stacked PR yet
Docs for the v2.0 format #370 (open, branch manifest-docs)
Branded field-name keys for compile-time dependsOn checks Not a must-have, larger implementation change
cli variant full hardening Shell-metacharacter denylist + describe notes shipped here. Remaining: argv-array form (executor-side spawn-not-shell), output-shape contract (replace jq path with declared field names + unwrap). Tied to the executor PR.
Track B skill PR databricks-agent-skills#79 — opens for skill author review; not required to merge before Track A

Note

Plugin authors continue to write manifest.json. See the docs PR (#370) for the authoring guide.

Note on the generated files diff

  1. Zod emits keys in a different order than the hand-written JSON Schema did. For example, you'll see "description" flip from before "type" to after "type", and "required": [...] arrays appear in different positions. z.toJSONSchema() has its own key ordering, and that's what now writes the file.
  2. Discovery is reshaped as a discriminated union. That's why $ref: "#/$defs/resourceRequirement" got expanded into giant oneOf: [...] blocks per resource type — the schema's actual shape changed, not just its layout.

Biome's JSON formatter only normalizes whitespace; it doesn't reorder keys. So the noise you're seeing is the cost of moving from hand-authored JSON Schema → Zod-generated JSON.

@atilafassina atilafassina marked this pull request as ready for review April 9, 2026 08:16
Copilot AI review requested due to automatic review settings April 9, 2026 08:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR upgrades the AppKit plugin manifest ecosystem to a v2.0 template manifest format to support smarter databricks apps init scaffolding, including field discovery metadata, post-scaffold user instructions, computed field origins, and semantic (cross-field) validation during plugin validate.

Changes:

  • Bump template plugin manifest version to 2.0 and add a top-level scaffolding descriptor.
  • Extend plugin/template schemas with discovery (CLI command-based value discovery) and postScaffold steps; generate/propagate computed origin into template manifests during sync.
  • Add semantic validation (dependsOn cycles/dangling refs, <PROFILE> placeholder, discovery/origin coherence, postScaffold structure) and associated tests/docs updates.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
template/appkit.plugins.json Updates template manifest to v2.0 and annotates built-in plugins with discovery/origin/postScaffold plus scaffolding descriptor.
packages/shared/src/schemas/template-plugins.schema.json Expands template manifest schema to support v2.0 and scaffolding descriptor; inlines field/requirement defs for origin.
packages/shared/src/schemas/plugin-manifest.schema.json Adds discovery to resource fields and postScaffold to plugin manifests.
packages/shared/src/schemas/plugin-manifest.generated.ts Updates generated TS types to include discovery/postScaffold types.
packages/shared/src/plugin.ts Re-exports new generated types (DiscoveryDescriptor, PostScaffoldStep).
packages/shared/src/cli/commands/plugin/validate/validate.ts Runs semantic validation and formats semantic errors/warnings in CLI output.
packages/shared/src/cli/commands/plugin/validate/validate-manifest.ts Implements semantic validation rules and issue formatting.
packages/shared/src/cli/commands/plugin/validate/validate-manifest.test.ts Adds test coverage for new semantic validation behavior.
packages/shared/src/cli/commands/plugin/sync/sync.ts Computes/injects origin, bumps template manifest to v2.0, and adds scaffolding descriptor on write.
packages/shared/src/cli/commands/plugin/sync/sync.test.ts Adds unit tests for origin computation.
packages/shared/src/cli/commands/plugin/manifest-types.ts Adds scaffolding descriptor types and exports new manifest-related types.
packages/appkit/src/plugins/lakebase/manifest.json Adds discovery descriptors and postScaffold steps to the lakebase built-in plugin manifest.
packages/appkit/src/plugins/genie/manifest.json Adds schema reference, discovery descriptor, and postScaffold steps to genie plugin manifest.
packages/appkit/src/plugins/files/manifest.json Adds discovery descriptor and postScaffold steps to files plugin manifest.
packages/appkit/src/plugins/analytics/manifest.json Adds discovery descriptor and postScaffold steps to analytics plugin manifest.
docs/static/schemas/template-plugins.schema.json Publishes updated template plugins schema for docs site.
docs/static/schemas/plugin-manifest.schema.json Publishes updated plugin manifest schema for docs site.
docs/static/appkit-ui/styles.gen.css Updates generated UI styles (tailwind output) used by docs UI.
docs/docs/api/appkit/Interface.ResourceFieldEntry.md Documents the new discovery field on ResourceFieldEntry.
docs/docs/api/appkit/Interface.PluginManifest.md Documents the new postScaffold field on PluginManifest.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/shared/src/cli/commands/plugin/validate/validate-manifest.ts Outdated
Comment thread packages/shared/src/schemas/plugin-manifest.generated.ts Outdated
Comment thread template/appkit.plugins.json Outdated
Comment thread packages/appkit/src/plugins/lakebase/manifest.json Outdated
@atilafassina atilafassina changed the base branch from main to pkosiec/template-artifact April 10, 2026 10:21
Base automatically changed from pkosiec/template-artifact to main April 14, 2026 07:52
@keugenek
Copy link
Copy Markdown
Contributor

@atilafassina this one looks ok and evolution, however little heavy code-wise (let me post comments from isaac about these) and mixing concepts little bit. What do you think of this:

Alternatives worth weighing

Zod as single source of truth (my default for a TS-first ecosystem):

  • Types, runtime validation, and semantic checks in one file.
  • zod-to-json-schema emits the $schema JSON for VSCode intellisense.
  • Cycle/dangling-ref checks become .refine() calls in the same module as the
    schema.
  • Deletes plugin-manifest.generated.ts and the Ajv layer.

TS-authored manifests compiled to JSON:

  • manifest.ts → defineManifest({...}) with branded types for field names
    (compile-time dependsOn checking, no DFS needed).
  • sync lowers to .json for non-TS consumers.
  • Strongest author ergonomics; agent still reads JSON.

Split concerns into two files:

  • manifest.json — strict contract (resources, fields, env), stable schema.
  • scaffold.yaml or .md — agent hints (discovery, prompts, postScaffold), free
    to evolve.
  • Today they're fused, which is why the schema keeps growing.

Copy link
Copy Markdown
Contributor

@keugenek keugenek left a comment

Choose a reason for hiding this comment

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

Overall direction looks right — v2.0 versioning via if/then is correct and co-located manifest.json per plugin is the right locality. A few shape-level concerns worth addressing before or soon after this lands. Happy to chat on any of them.

Comment thread docs/static/schemas/plugin-manifest.schema.json Outdated
Comment thread packages/shared/src/cli/commands/plugin/sync/sync.ts Outdated
Comment thread docs/static/schemas/template-plugins.schema.json Outdated
Comment thread docs/static/schemas/template-plugins.schema.json Outdated
Comment thread .claude/scheduled_tasks.lock Outdated
atilafassina added a commit that referenced this pull request Apr 21, 2026
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>
@MarioCadenas MarioCadenas requested a review from a team as a code owner May 6, 2026 10:59
atilafassina added a commit that referenced this pull request May 7, 2026
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>
@atilafassina atilafassina marked this pull request as draft May 7, 2026 20:12
@atilafassina atilafassina removed the request for review from arsenyinfo May 7, 2026 20:12
…tScaffold, and scaffolding

Xavier loop: iteration 1 — Phase 1 (Schema Definitions & Type Generation)

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…te manifest emission

Xavier loop: iteration 2 — Phase 2 (Origin Computation & Sync Enrichment)

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…, and postScaffold

Xavier loop: iteration 3 — Phase 3 (Semantic Validation)

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…ostScaffold steps

Xavier loop: iteration 4 — Phase 4 (Core Plugin Manifest Annotations)

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…t 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>
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>
… 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>
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>
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>
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>
# Conflicts:
#	.github/workflows/ci.yml
#	docs/docs/api/appkit/index.md
#	docs/docs/api/appkit/typedoc-sidebar.ts
#	docs/package.json
#	knip.json
#	packages/appkit/src/plugins/genie/manifest.json
#	template/appkit.plugins.json
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>
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>
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>
…ecks

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>
…tion

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>
@atilafassina atilafassina marked this pull request as ready for review May 19, 2026 20:52
Comment thread packages/appkit/src/plugins/analytics/manifest.json
Comment thread packages/appkit/src/plugins/files/manifest.json
Comment thread packages/shared/src/cli/commands/plugin/sync/sync.ts
Comment thread tools/generate-json-schema.ts Outdated
Comment thread tools/generate-registry-types.ts Outdated
Comment thread template/appkit.plugins.json Outdated
Comment thread template/appkit.plugins.json
Comment thread docs/docs/api/appkit/Interface.PluginManifest.md
Comment thread packages/appkit/src/plugins/lakebase/manifest.json Outdated
- 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>
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>
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>
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>
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>
Comment thread template/appkit.plugins.json
atilafassina and others added 2 commits May 22, 2026 10:02
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>
atilafassina added a commit that referenced this pull request May 22, 2026
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>
* 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>
@atilafassina atilafassina merged commit 7542a03 into main May 22, 2026
9 checks passed
@atilafassina atilafassina deleted the manifest-ax branch May 22, 2026 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants