Write or modify CyanPrint resolver code in JavaScript. Use when the user asks to change conflict resolution logic, modify merge strategies, handle file origins, or change resolution behavior. Covers entry point (StartResolverWithLambda), ResolverInput/ResolverOutput, ResolvedFile, and FileOrigin. Must ensure commutativity and associativity (sort, unique, deterministic ordering).
Install
npx skillscat add atomicloud/ketone-new-cyanprint/writing-resolver-javascript Install via the SkillsCat registry.
SKILL.md
Writing this Resolver (JavaScript)
Entry Point
const { StartResolverWithLambda } = require('@atomicloud/cyan-sdk');
StartResolverWithLambda(async input => {
// Resolve conflict
return { path, content };
});ResolverInput
// input.config: { [key: string]: unknown }
// input.files: ResolvedFile[]ResolvedFile
All files entries have the same path -- that is the conflict being resolved:
// file.path: string
// file.content: string
// file.origin: FileOriginFileOrigin
// file.origin.template: string -- Which template produced this file
// file.origin.layer: number -- Layer number -- IMPORTANT: number, NOT stringCritical: layer is a number, not a string. Compare numerically, never as string.
ResolverOutput
Return a single resolved file:
// { path: string, content: string }Commutativity and Associativity
CyanPrint may call the resolver with files in any order. Your result must be identical regardless of input ordering.
Pattern 1: Sort before processing
const sorted = [...input.files].sort((a, b) => {
if (a.origin.layer !== b.origin.layer) return a.origin.layer - b.origin.layer;
return a.origin.template.localeCompare(b.origin.template);
});Pattern 2: Deduplicate after merge
const allItems = sorted.flatMap(f => JSON.parse(f.content).items);
const unique = [...new Set(allItems)].sort();Pattern 3: Deterministic priority
// Highest layer number wins -- deterministic regardless of input order
const winner = input.files.reduce((best, f) => (f.origin.layer > best.origin.layer ? f : best));Resolution Strategies
Last-Write Wins (by layer)
const sorted = [...input.files].sort((a, b) => a.origin.layer - b.origin.layer);
const last = sorted[sorted.length - 1];
return { path: last.path, content: last.content };Deep Merge (JSON)
const sorted = [...input.files].sort((a, b) => a.origin.layer - b.origin.layer);
let merged = {};
for (const file of sorted) {
merged = deepMerge(merged, JSON.parse(file.content));
}
return { path: input.files[0].path, content: JSON.stringify(merged, null, 2) };Entry Point Skeleton
const { StartResolverWithLambda } = require('@atomicloud/cyan-sdk');
StartResolverWithLambda(async input => {
const { config, files } = input;
if (files.length === 0) throw new Error('Resolver received no files — at least 1 file is required');
const uniquePaths = new Set(files.map(f => f.path));
if (uniquePaths.size > 1)
throw new Error(
`Resolver received files with different paths: ${[...uniquePaths].join(', ')} — all files must have the same path`,
);
const path = files[0].path;
// Sort for commutativity (layer ascending, then template name)
const sorted = [...files].sort((a, b) => {
if (a.origin.layer !== b.origin.layer) return a.origin.layer - b.origin.layer;
return a.origin.template.localeCompare(b.origin.template);
});
// TODO: Implement resolution logic
const content = sorted[sorted.length - 1].content;
return { path, content };
});Key Rules
- All
filesentries have the samepath-- that is the conflict being resolved FileOrigin.layeris anumber-- compare numerically, never as string- Return a single
{ path, content }-- the resolved file - Ensure commutativity -- sort inputs before processing, deduplicate outputs
- Ensure associativity -- result must be same whether resolved all-at-once or in pairs
- Validate input -- reject empty files list and mismatched paths with an error