SyncPoint's core protocol is resource-type agnostic. Files, images, database tables, API endpoints — any addressable resource can be claimed, tracked, and conflict-checked through the same primitives.
Plugins teach SyncPoint how to handle specific resource types and operation types by registering extensions at startup. There are four extension point categories.
A ResourceMatcher tells the core conflict detector how two locators of the same resource type overlap.
import { registerResourceMatcher } from "syncpoint-core";
registerResourceMatcher({
type: "file",
locatorsOverlap(a: string, b: string): boolean {
// e.g. "src/auth" overlaps "src/auth/session.ts"
const na = a.replace(/\/+$/, "");
const nb = b.replace(/\/+$/, "");
if (na === nb) return true;
if (na.startsWith(nb + "/") || nb.startsWith(na + "/")) return true;
return false;
},
});Interface (syncpoint-core):
interface ResourceMatcher {
type: string;
locatorsOverlap(a: string, b: string): boolean;
}Registry functions (all exported from syncpoint-core):
| Function | Purpose |
|---|---|
registerResourceMatcher(m) |
Register a matcher for a resource type (overwrites previous) |
getResourceMatcher(type) |
Retrieve the registered matcher, or undefined |
clearResourceMatcherRegistry() |
Remove all matchers (testing only) |
How core uses it: resourceLocatorsOverlap(a, b) delegates to the registered matcher for a.type. If no matcher is registered, it falls back to exact locator equality. detectResourceClaimConflicts(claims) calls resourceLocatorsOverlap for every pair of active claims.
An OperationValidator runs domain-specific checks when an operation is submitted or checked. Validators are matched by operation type and/or resource type.
import { registerOperationValidator } from "syncpoint-core";
registerOperationValidator({
name: "image_dimensions",
operationTypes: ["image_edit"],
resourceTypes: ["image"],
validate(ctx) {
const valid = ctx.operation.targetResources.some(r => r.metadata.includes("width"));
return [{
check: "image_dimensions",
passed: valid,
detail: valid ? "Dimensions specified" : "Missing image dimensions",
}];
},
});Interface (syncpoint-core):
interface OperationValidator {
name: string;
operationTypes: string[]; // empty = applies to all types
resourceTypes: string[]; // empty = applies to all types
validate(ctx: OperationValidationContext): OperationCheckItem[];
}
interface OperationValidationContext {
operation: Operation;
actorClaims: ResourceClaim[]; // claims belonging to the operation's actor
allActiveClaims: ResourceClaim[]; // claims across all actors
payload?: string; // optional operation payload, if supplied by the caller
}
interface OperationCheckItem {
check: string; // machine-readable check name
passed: boolean;
detail: string; // human-readable explanation
}Registry functions (all exported from syncpoint-core):
| Function | Purpose |
|---|---|
registerOperationValidator(v) |
Add a validator to the registry |
getValidatorsForOperation(type, resourceTypes?) |
Query applicable validators |
runOperationValidation(ctx) |
Run all matching validators, return combined OperationCheckItem[] |
clearValidatorRegistry() |
Remove all validators (testing only) |
Matching rules: A validator fires when:
- Its
operationTypesis empty (wildcard) or contains the operation'stype, AND - Its
resourceTypesis empty (wildcard) or overlaps with the operation'stargetResourcestypes.
The current application opCheck() path passes operation metadata and resource claims to validators. The core validation interface supports optional payload, but callers must explicitly supply it when using runOperationValidation() directly.
A ScopeMatcher tells the projection engine how to match appliesTo scope fields against a task's working context. This controls which Project Memory entries are relevant to a given agent's work.
import { registerScopeMatcher } from "syncpoint-core";
registerScopeMatcher({
field: "tables",
findOverlaps(patterns: string[], targets: string[]): string[] {
return targets.filter(t =>
patterns.some(p => t === p || t.startsWith(p + "."))
);
},
});Interface (syncpoint-core):
interface ScopeMatcher {
field: string;
findOverlaps(patterns: string[], targets: string[]): string[];
}Registry functions (all exported from syncpoint-core):
| Function | Purpose |
|---|---|
registerScopeMatcher(m) |
Register a matcher for a scope field (overwrites previous) |
getScopeMatcher(field) |
Retrieve the registered matcher, or undefined |
clearScopeMatcherRegistry() |
Remove all matchers (testing only) |
Built-in registrations (_scope-matchers.ts in syncpoint-server):
| Field | Semantics |
|---|---|
files |
Prefix/glob overlap against working resource locators |
modules |
Prefix/glob overlap against current module context |
How core uses it: During compileProjection(), each memory's appliesTo JSON is parsed (e.g. {"files": ["src/**"]}). For each field, the registered ScopeMatcher.findOverlaps() checks if any pattern overlaps with the task's working resources or module context. Unmatched memories are excluded from the projected reality.
A ConstraintRuleEvaluator handles domain-specific constraint evaluation. When a Project Memory entry has kind: "hard_constraint" and a validatorType, the constraint runtime dispatches to the matching evaluator.
import { registerConstraintRuleEvaluator } from "syncpoint-core";
registerConstraintRuleEvaluator({
ruleType: "table_forbidden",
evaluate(input, item, spec) {
const locators = input.touchedResources
?.filter(r => r.type === "db_table")
.map(r => r.locator) ?? [];
const forbidden = ((item.scope as any)?.tables ?? []) as string[];
const overlap = locators.filter(l => forbidden.includes(l));
if (overlap.length > 0) {
return {
rule: "table_forbidden",
message: `Touches forbidden tables: ${overlap.join(", ")}`,
sourceMemoryId: item.source.sourceMemoryId,
projectionId: input.projection.projectionId,
evidence: overlap,
};
}
return null;
},
});Interface (syncpoint-core):
interface ConstraintRuleEvaluator {
ruleType: string;
evaluate(
input: ConstraintInput,
item: ProjectionItem,
spec: ConstraintRuntimeSpec,
): ConstraintViolation | null;
}Registry functions (all exported from syncpoint-core):
| Function | Purpose |
|---|---|
registerConstraintRuleEvaluator(e) |
Register an evaluator for a rule type (overwrites previous) |
getConstraintRuleEvaluator(ruleType) |
Retrieve the registered evaluator, or undefined |
clearConstraintRuleEvaluatorRegistry() |
Remove all evaluators (testing only) |
Known rule types:
| Rule Type | Semantics |
|---|---|
file_forbidden |
Reserved validator type; blocks only when a matching evaluator is registered |
module_forbidden |
Reserved validator type; blocks only when a matching evaluator is registered |
require_review |
Core-handled rule for operation submit/apply contexts |
custom |
Reserved custom type; advisory unless claimed by runtime logic |
How core uses it: evaluateConstraints() iterates over projected hard_constraint entries. If an entry has a validatorType that is handled by core or has a matching registered ConstraintRuleEvaluator, the runtime evaluates it. If the evaluator returns a ConstraintViolation, it becomes a blocker. UnknownValidatorTypeError is thrown earlier by Project Memory create/update validation when a blocking hard_constraint uses an unrecognized validatorType.
A generic point-in-time snapshot of an actor's work state, referencing the resources they are working with. Generalizes checkpoint + capsule concepts for any resource type.
interface StateSnapshot {
id: string;
actorId: string;
taskId: string;
sessionId: string;
summary: string;
resourceRefs: ResourceRef[];
createdAt: string;
}The universal resource reference. Every resource in SyncPoint is addressed by a (type, locator) pair.
interface ResourceRef {
type: string; // e.g. "file", "image", "db_table", "api_endpoint"
locator: string; // type-specific address, e.g. "src/auth.ts", "assets/logo.png"
metadata: string; // optional type-specific metadata
id?: string; // optional stable ID
}A tracked unit of work with a status lifecycle.
enum OperationStatus {
DRAFT → SUBMITTED → APPROVED → APPLIED
→ REJECTED → SUBMITTED (resubmit)
→ CONFLICTING → SUBMITTED (resubmit)
→ CANCELLED (from any non-terminal)
}
interface Operation {
id: string;
type: string; // e.g. "code_patch", "image_edit"
actorId: string;
taskId: string;
sessionId: string;
title: string;
summary: string;
targetResources: ResourceRef[]; // resources this operation touches
payloadRef: string; // reference to payload content
status: OperationStatus;
checkResult: string;
decisionSummary: string;
createdAt: string;
updatedAt: string;
}An ownership declaration over one or more resources.
interface ResourceClaim {
id: string;
actorId: string;
taskId: string;
sessionId: string;
resources: ResourceRef[];
mode: "exclusive" | "shared";
status: "ACTIVE" | "RELEASED";
createdAt: string;
releasedAt: string;
}syncpoint-plugin-code is the reference implementation. It teaches SyncPoint how to handle type: "file" resources and type: "code_patch" operations.
| Module | Exports | Purpose |
|---|---|---|
file-resource.ts |
parseClaimPaths, pathsOverlap, filePathsToResourceRefs, resourceRefsToFilePaths |
File path parsing, glob-aware overlap, ResourceRef conversion |
code-patch.ts |
extractTouchedFiles, isValidPatchFormat, findUncoveredFiles, findConflictingClaims, runCodePatchChecks |
Unified diff parsing and claim checks |
validators.ts |
codePatchFormatValidator, codePatchClaimCoverageValidator, codePatchNoHardConflictValidator |
OperationValidators for code_patch + file |
index.ts |
registerCodePlugin() |
One-call registration entry point |
| Name | Checks |
|---|---|
code_patch_format |
Payload is a valid unified diff when payload text is supplied |
code_patch_claim_coverage |
All touched files are covered by the actor's active claims |
code_patch_no_hard_conflict |
No other agent's exclusive claims conflict with touched files |
import { registerCodePlugin } from "syncpoint-plugin-code";
registerCodePlugin();
// Safe to call multiple times — idempotent via name-based dedup.syncpoint-plugin-generic-agent teaches SyncPoint how to handle non-code resource types. It is the protocol-level plugin for any shared resource that is not a source file.
| Module | Exports | Purpose |
|---|---|---|
matchers.ts |
GENERIC_RESOURCE_MATCHERS |
URI-path-prefix overlap for artifact, binary_asset, document, design_asset, dataset_slice |
validators.ts |
genericClaimCoverageValidator, genericNoHardConflictValidator, genericPayloadPresentValidator |
Claim coverage, hard conflict, payload presence for generic operations |
constraint-evaluators.ts |
resourceForbiddenEvaluator |
Blocks operations touching resources listed in a resource_forbidden hard constraint |
scope-matchers.ts |
resourcesScopeMatcher, assetTypesScopeMatcher |
Scope field matching for resources and assetTypes in appliesTo |
index.ts |
registerGenericAgentPlugin() |
One-call idempotent registration |
artifact, binary_asset, document, design_asset, dataset_slice
artifact_update, artifact_review, artifact_transform, asset_generate, asset_edit, asset_update
| Name | Checks |
|---|---|
generic_claim_coverage |
All target resources are covered by the actor's active claims |
generic_no_hard_conflict |
No other agent's exclusive claims conflict with target resources |
generic_payload_present |
Operation has a payload or payloadRef |
| Rule Type | Behavior |
|---|---|
resource_forbidden |
Blocks when operation's targetResources overlap with forbidden resource locators defined in a hard_constraint's appliesTo.resources scope |
import { registerGenericAgentPlugin } from "syncpoint-plugin-generic-agent";
registerGenericAgentPlugin();
// Safe to call multiple times — idempotent.Auto-registered at server startup via _plugin-init.ts alongside syncpoint-plugin-code.
Starting with PR5, opCheck() and opApply() integrate the Constraint Runtime as a hard blocker alongside the existing OperationValidator pipeline.
opCheck(operationId)
│
├── runOperationValidation() → OperationCheckItem[]
│ └── generic_claim_coverage, generic_no_hard_conflict, generic_payload_present, ...
│
├── evaluateConstraints() → ConstraintDecision
│ └── resource_forbidden, do_not_touch, projection gates, ...
│
├── if blockers:
│ ├── add "constraint_runtime" check item (passed: false)
│ ├── write constraintViolations to checkResult
│ └── transition SUBMITTED → CONFLICTING
│
└── return { operation, checkResult }
opApply(operationId)
│
├── validate transition (APPROVED → APPLIED)
│
├── evaluateConstraints(action: "operation_apply")
│ └── if blockers → throw Error (apply rejected)
│
└── update status → APPLIED
This ensures that even if a constraint was added between check and apply, the operation is re-evaluated before it takes effect.
Pick a type string (e.g. "db_table", "api_endpoint", "binary_asset").
If your resource type has overlap semantics beyond exact string equality, register a matcher:
import { registerResourceMatcher } from "syncpoint-core";
registerResourceMatcher({
type: "db_table",
locatorsOverlap(a, b) {
// "users" overlaps "users", "users.*" overlaps "users.email"
if (a === b) return true;
if (a.endsWith(".*")) return b.startsWith(a.slice(0, -1));
if (b.endsWith(".*")) return a.startsWith(b.slice(0, -1));
return false;
},
});Define validators for your operation type:
import { registerOperationValidator } from "syncpoint-core";
registerOperationValidator({
name: "migration_has_rollback",
operationTypes: ["db_migration"],
resourceTypes: ["db_table"],
validate(ctx) {
const hasRollback = ctx.operation.summary.includes("rollback");
return [{
check: "migration_has_rollback",
passed: hasRollback,
detail: hasRollback
? "Migration includes rollback section"
: "Migration is missing a rollback section",
}];
},
});Follow the registerCodePlugin() pattern — one idempotent function that registers all matchers and validators:
import { getValidatorsForOperation, registerOperationValidator } from "syncpoint-core";
const VALIDATORS = [myValidator1, myValidator2];
export function registerDbPlugin(): void {
const existing = new Set(
getValidatorsForOperation("db_migration", ["db_table"]).map(v => v.name),
);
for (const v of VALIDATORS) {
if (!existing.has(v.name)) {
registerOperationValidator(v);
existing.add(v.name);
}
}
}packages/syncpoint-plugin-yourtype/
├── package.json # depends on syncpoint-core (workspace:*)
├── tsconfig.json
├── vitest.config.ts
└── src/
├── index.ts # registerYourPlugin() + re-exports
├── your-resource.ts # ResourceRef helpers, overlap logic
├── your-operation.ts # operation-specific helpers
└── validators.ts # OperationValidator implementations
Plugin registers at startup
│
├── ResourceMatcher ──────► detectResourceClaimConflicts()
│ └── used by rcClaim, rcDetectConflicts
│
├── OperationValidator ───► runOperationValidation()
│ └── used by opCheck, opSubmit
│
├── ScopeMatcher ─────────► compileProjection()
│ └── appliesTo filtering for Project Memory
│
└── ConstraintRuleEvaluator ► evaluateConstraints()
└── typed hard_constraint enforcement
└── used by opCheck, opApply (PR5)
When an agent calls syncpoint_resource_claim, core's conflict detection delegates to the registered ResourceMatcher for the resource type. When an agent calls syncpoint_operation_submit or syncpoint_operation_check, core's validation pipeline runs all matching OperationValidators and the Constraint Runtime evaluates all projected constraints against the operation's target resources. During loopResume, the projection engine uses ScopeMatchers to filter relevant Project Memory, and the constraint runtime uses ConstraintRuleEvaluators to enforce typed constraints.
If no extension is registered for a given type, core falls back to safe defaults:
- No ResourceMatcher → exact locator equality for overlap detection
- No OperationValidator → operation passes all checks (empty check result)
- No ScopeMatcher → exact string matching on scope field values
- No ConstraintRuleEvaluator → reserved typed constraints are not claimed at runtime and fall back to advisory unless core handles the rule
The plugin API is functional and auto-wired:
syncpoint-plugin-codeis registered at server startup via_plugin-init.ts. FileResourceMatcher, threeOperationValidators forcode_patch+file, and scope matchers forfiles/modulesare active.syncpoint-plugin-generic-agentis registered at server startup via_plugin-init.ts. FiveResourceMatchers for generic resource types, threeOperationValidators for generic operations,resource_forbiddenConstraintRuleEvaluator, and scope matchers forresources/assetTypesare active.opCheck()andopApply()run both theOperationValidatorpipeline and theConstraint Runtimeas hard blockers (PR5).- Five
ConstraintRuleEvaluatortypes are recognized:file_forbidden,module_forbidden,resource_forbidden,require_review,custom.