PR 158 Merge Concerns
PR #158 Merge Concerns — export_import into main
Written for AI agents. See Log Methodology Note below for details.
Background
PR #158 (export_import branch) introduces the Figma vendor integration and enhanced plugin resolution. Meanwhile, main has evolved significantly since the branch diverged (merge base df492a19), adding:
- Headless component props & repeater support (Design Log #84, PR #159)
- Dynamic contracts (Design Log #80)
jay-stack setupcommand (Design Log #87)- Structured logging (
getLogger()replacingconsole.*) - Dev server test mode (Design Log #81)
- Agent kit (Design Log #85)
16 files were modified on both branches. This document catalogs merge concerns, duplicate work, and decisions needed before merging.
Concern 1: Plugin/Contract Model Divergence — RESOLVED
Files: editor-protocol/lib/protocol.ts, editor-handlers.ts, plugin-resolution.ts
What happened
| Concept | main | PR branch |
|---|---|---|
Plugin type |
{ manifest: PluginManifest, location: { type, path?, module? } } — full manifest from compiler-shared |
{ name: string, contracts: Contract[] } — simplified |
ContractSchema |
Present | Replaced by Contract (adds trackBy, async, phase on tags) |
InstalledApp |
Still present (legacy) | Removed |
InstalledAppContracts |
Still present | Removed |
PluginManifest in protocol |
Re-exported from compiler-shared | Removed from protocol |
ProjectPage.contractSchema |
Present | Renamed to ProjectPage.contract |
Decision: Take the PR's approach
Verified that InstalledApp, InstalledAppContracts, contractSchema, and the full Plugin type are only consumed by editor-handlers.ts and the protocol layer itself (definition, constructors, types re-export). No other packages depend on them.
- Q1: Remove
InstalledAppfully (PR's approach) - Q2: Use PR's simplified
Plugin { name, contracts } - Q3: Rename
contractSchema→contract(PR's approach)
Concern 2: Plugin Resolution Architecture — RESOLVED
File: compiler-shared/lib/plugin-resolution.ts
What happened
| Aspect | main | PR branch |
|---|---|---|
resolveLocalPlugin / resolveNpmPlugin |
Exported, inline logic, supports dynamic contracts | Refactored to use manifest helpers, made private, no dynamic contract support |
dynamic_contracts type |
DynamicContractConfig | DynamicContractConfig[] with exported interface |
Keeps original inline type |
| New APIs | findDynamicContract() |
resolvePluginManifest(), resolveLocalPluginManifest(), resolveNpmPluginManifest() |
LOCAL_PLUGIN_PATH |
Not exported | Exported as constant |
slugs field on PluginManifest |
Not present | Added to contract definitions in plugin.yaml |
params field on Contract |
Added (Design Log #85) | Not present |
setup field on PluginManifest |
Added (Design Log #87) | Not present |
| Error handling | Improved messages with dynamic contract prefixes (e.g. list/*) |
Structured WithValidations<T> responses |
Decision
Resolution structure: Take the PR's approach for resolveLocalPlugin/resolveNpmPlugin refactoring. Verify no logic fixes from main are lost in the plugin.yaml loading path.
APIs: Keep both sets of new APIs:
- PR:
resolvePluginManifest(),resolveLocalPluginManifest(),resolveNpmPluginManifest() - Main:
findDynamicContract()
Preserve from main:
DynamicContractConfiginterface andDynamicContractConfig | DynamicContractConfig[]type on manifestsetupfield on PluginManifest (Design Log #87)- Dynamic contract resolution logic (port into PR's refactored helpers)
Preserve from PR:
LOCAL_PLUGIN_PATHexported constant- Manifest-only resolution helpers
Drop from PR:
slugsfield on PluginManifest contracts — main usesparamson the Contract file itself (Design Log #85), which is the chosen approach. Params live in the.jay-contractfile, not inplugin.yaml.- Remove
slugsvalidation fromvalidate-plugin.tsas well.
Error handling: Both branches already use WithValidations<T>, so merging error handling is straightforward — combine main's improved error messages (with dynamic contract prefixes) with PR's structured validation responses.
Concern 3: Headless Components vs Vendor System — RESOLVED
Files: jay-html-builder.ts, binding-analysis.ts, protocol.ts
What happened
Main introduced instance-only headless components (Design Log #84):
<!-- Instance-only: no key, used via <jay:product-card> -->
<script type="application/jay-headless" plugin="wix-stores" contract="product-card"></script>
<jay:product-card productId="prod-123">
<h1>{name}</h1>
</jay:product-card>
The PR's vendor system assumes all headless components have a key:
| Location | Issue |
|---|---|
jay-html-builder.ts HeadlessComponent |
key: string is required |
jay-html-builder.ts lines 205-211 |
Only adds components when comp.plugin && comp.contract && comp.key |
binding-analysis.ts lines 141-149 |
key = usedComponent.key — undefined for instance-only |
binding-analysis.ts lines 172-178 |
Builds fullPath as [key, ...tagPath] — produces "undefined.name" for instance-only |
protocol.ts usedComponents[].key |
Typed as string (required), should be string | undefined |
Decision: Known limitation — filter out, don't break
This is not a technical bug to fix now. The PR does not handle nested (instance-only) headless components because:
- We don't yet know how this support should look on the design-tool side of the editor protocol (e.g., how Figma represents a
<jay:product-card>instance with props) - The product-level design for headless components in design tools needs to come first
For the merge:
- The vendor system should filter out or ignore instance-only headless components (without
key) rather than producing invalid output like"undefined.name" - Make
usedComponents[].keyoptional (string | undefined) in the protocol types for correctness - Document as a known missing feature: "Vendor export does not yet support instance-only headless components (
<jay:xxx>withoutkey). Requires design for how design tools represent headless component instances and props."
Not blocking merge. Key-based headless components (the pre-Design Log #84 model) remain fully supported.
Concern 4: Logging Migration (MEDIUM)
Files: editor-handlers.ts, editor-server.ts, connection-manager.ts, server.ts
What happened
Main migrated all console.* calls to getLogger() from @jay-framework/logger. The PR still uses console.* throughout.
Affected areas
editor-handlers.ts— ~25 call sites on main now use logger; PR rewrites the file withconsole.*editor-server.ts— main uses logger; PR adds new export/import handlers withconsole.*connection-manager.ts— main uses logger; PR adds types but keepsconsole.*server.ts— main useslog.important(); PR usesconsole.logfor vendor registration
Resolution
Straightforward but tedious: after merging PR's structural changes, replace all console.* with getLogger().*. No decision needed, just execution.
Concern 5: editor-handlers.ts Structural Rewrite — RESOLVED
File: stack-cli/lib/editor-handlers.ts
What happened
Main changed only logging (~25 call sites). PR rewrote major sections:
| Function | main | PR |
|---|---|---|
parseContractFile |
Logging change only | Replaced by loadAndExpandContract() |
resolveLinkedTags |
Logging change only | Replaced by expandContractTags() |
extractHeadlessComponents |
Uses installedApps + installedAppContracts |
Replaced by extractHeadlessComponentsFromJayHtml() using parseJayFile |
scanInstalledApps |
Present | Removed |
scanInstalledAppContracts |
Present | Removed |
scanPlugins |
Logging change only | Rewritten to use resolvePluginManifest |
New: onExport |
N/A | Handles vendor document export |
New: onImport |
N/A | Handles vendor document import |
New: pageUrlToDirectoryPath |
N/A | URL → filesystem path conversion |
New: convertContractToProtocol |
N/A | Compiler contract → protocol contract |
Decision: Take the PR's approach
The PR's structural rewrites are the base. The PR's parseJayFile-based headless extraction is more correct than main's HTML parsing. What still needs porting from main into the PR's code:
- Replace all
console.*withgetLogger().* - Port dynamic contract support into the rewritten
scanPlugins
Concern 6: Test Mock Compatibility (LOW)
Files: contract-compiler.test.ts, parse-jay-file.unit.test.ts
What happened
Both branches add new methods to the mock JayImportResolver:
- Main adds
loadPluginContractstubs - PR adds
resolvePluginManifeststubs
Resolution
Include both stubs in the merged mock objects. Low effort, no decisions needed.
Concern 7: Documentation Overlap — RESOLVED
Files: jay-html.md, building-jay-packages.md, README.md
What happened
jay-html.md: Main documents instance-based headless and boolean attributes. PR documents contract references, sandbox attribute, and validation. Both edit the headless import section.building-jay-packages.md: Main documents dynamic contract materialization. PR documents slugs and URL parameters.README.md: Main adds CLI/testing links. PR adds design tool integration links.
Decision
- Reconcile all docs — merge both sets of documentation.
jay-html.mdheadless section: merge to cover key-based, instance-based, and vendor-exported headless imports.building-jay-packages.md: PR'sslugsdocumentation must be replaced with main'sparamsapproach (params live in the.jay-contractfile, notplugin.yaml). Update examples accordingly.README.md: merge both sets of links (different sections, low conflict).
Concern 8: Build/Config Changes — RESOLVED
Files: .gitignore, route-scanner/package.json, route-scanner/vite.config.ts
.gitignore: Main addsagent-kit, PR changes.yarn/install-state.gz→.yarn/. Both are independent — keep both.route-scanner: PR changes output from CJS to ESM (dist/index.js→dist/index.mjs,formats: ['es']). Main has no changes here.
Decision
Accept the ESM change. Verify the dev server works after the switch (route-scanner is used during route scanning at dev time). If the dev server starts and routes resolve correctly, the change is safe.
Summary: Decision Matrix
| # | Question | Options | Decision |
|---|---|---|---|
| Q1 | Keep or remove InstalledApp? | (a) Keep deprecated (b) Remove fully | DECIDED: (b) Remove fully. Only used by editor-handlers (+ protocol definition). PR's parseJayFile approach is more correct. |
| Q2 | Protocol Plugin shape? | (a) Main's full manifest (b) PR's simplified | DECIDED: (b) PR's { name, contracts }. editor-handlers is the sole consumer of the protocol Plugin type. |
| Q3 | Rename contractSchema → contract? | (a) Yes (b) No | DECIDED: (a) Only used in editor-handlers + protocol + test. PR's rename is clean. |
| Q4 | Plugin resolution merge strategy? | Manual integration | DECIDED: PR's refactored structure + main's dynamic contracts + both API sets. Drop PR's slugs (use main's params in contract file). |
| Q5 | Fix vendor for optional key? | (a) Before merge (b) Post-merge | DECIDED: Filter out instance-only headless (no key) in vendor code. Make usedComponents[].key optional. Document as known limitation pending design-tool UX design. |
| Q6 | Vendor props support? | (a) Before merge (b) Post-merge | DECIDED: Not in scope. We need product-level design for how design tools represent headless instances + props before implementing. |
| Q7 | Route-scanner ESM change? | Verify consumers | DECIDED: Accept ESM. Verify dev server works with the change. |
Guiding principle
For editor-handlers.ts and anything only consumed by it, the PR's approach wins.
Verified scope:
InstalledApp,InstalledAppContracts,contractSchema,scanInstalledApps,scanInstalledAppContracts,extractHeadlessComponents,resolveLinkedTags,parseContractFile— all exclusively used within editor-handlers.ts and its protocol definitions. No other packages depend on them. The PR's replacements (extractHeadlessComponentsFromJayHtml,loadAndExpandContract,expandContractTags, simplifiedPlugin/Contract,onExport/onImport) are adopted directly.What still needs porting from main into the PR's structure:
getLogger()logging (replaceconsole.*)- Dynamic contract support in
scanPlugins/ plugin resolution- Dev server test mode in
server.ts- Agent kit support
Merge Plan (Proposed Order)
Phase 1: Foundation (plugin-resolution.ts)
- Adopt PR's manifest helpers and
WithValidationserror handling - Port main's
DynamicContractConfig,findDynamicContract(), andsetupfield - Keep
LOCAL_PLUGIN_PATHexport andslugsfrom PR - Run plugin-resolution tests
Phase 2: Protocol (protocol.ts)
- Use PR's simplified
PluginandContracttypes - Remove
InstalledAppandInstalledAppContracts - Add PR's
ExportMessage,ImportMessage,ExportResponse,ImportResponse - Keep main's re-exports where needed for internal use
- Make
usedComponents[].keyoptional (string | undefined)
Phase 3: Editor Handlers (editor-handlers.ts)
- Use PR's structural rewrites as base
- Replace all
console.*withgetLogger().* - Port main's dynamic contract support into
scanPlugins - Add PR's
onExportandonImporthandlers - Run editor-handler tests
Phase 4: Compiler & Tests
- Merge
jay-html-parser.tschanges (both headless and import detection) - Merge
jay-import-resolver.ts(bothloadPluginContractandresolvePluginManifest) - Update test mocks to include both new resolver methods
- Run full test suite
Phase 5: Server, Client, Docs
- Merge
server.ts(test mode + vendor registration + logger) - Merge
editor-server.ts(logger + export/import handlers) - Merge
connection-manager.ts(logger + message types) - Merge documentation files
- Merge
.gitignore
Phase 6: Verification
- Run full test suite
- Manual test with
jay devin fake-shop - Verify vendor registration log message
- Build all packages
Verification Criteria
- All existing tests pass (no regressions from main)
- All PR tests pass (vendor, plugin-resolution, editor-handlers)
- Dynamic contracts still work (main's feature)
- Headless component props and instances still work (main's feature)
- Figma vendor export works for key-based headless components
-
getLogger()used everywhere (noconsole.*in production code) -
jay-stack setupcommand still works (main's feature) - Dev server test mode still works (main's feature)
- Agent kit still works (main's feature)
- No TypeScript compilation errors
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.