Write or modify CyanPrint resolver code in TypeScript. 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-typescript Install via the SkillsCat registry.
SKILL.md
Writing this Resolver (TypeScript)
Entry Point
import { StartResolverWithLambda, type ResolverInput, type ResolverOutput } from '@atomicloud/cyan-sdk';
StartResolverWithLambda(async (input: ResolverInput): Promise<ResolverOutput> => {
// Resolve conflict
return { path, content };
});ResolverInput
interface ResolverInput {
config: Record<string, unknown>;
files: ResolvedFile[];
}ResolvedFile
All files entries have the same path -- that is the conflict being resolved:
interface ResolvedFile {
path: string;
content: string;
origin: FileOrigin;
}FileOrigin
interface FileOrigin {
template: string; // Which template produced this file
layer: number; // Layer number -- IMPORTANT: number, NOT string
}Critical: layer is a number, not a string. Compare numerically, never as string.
ResolverOutput
Return a single resolved file:
interface ResolverOutput {
path: string; // Same path as input files
content: string; // Resolved content
}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
import { StartResolverWithLambda } from '@atomicloud/cyan-sdk';
import type { ResolverInput, ResolverOutput } from '@atomicloud/cyan-sdk';
StartResolverWithLambda(async (input: ResolverInput): Promise<ResolverOutput> => {
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`,
);
// All files have the same path -- resolve the conflict
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