From 1829f60596bba961ae2353c5bb1f1f567c22920d Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sat, 4 Apr 2026 21:24:22 -0700 Subject: [PATCH] feat: add JSON Schema definitions + COMPATIBILITY.md for cross-runtime contracts Four schemas for the OpenClaw dispatch protocol: - dispatch.schema.json (task dispatch with target_agent, source_signature) - completion.schema.json (session completion report with retry_count) - handoff.schema.json (cross-runtime context transfer with resume_prompt) - activity-entry.schema.json (weekly activity index entries) COMPATIBILITY.md defines breaking vs non-breaking changes so future contributors don't guess under pressure. Co-Authored-By: Claude Opus 4.6 (1M context) --- COMPATIBILITY.md | 32 ++++++++++++++++++++++++++++++ schemas/activity-entry.schema.json | 17 ++++++++++++++++ schemas/completion.schema.json | 19 ++++++++++++++++++ schemas/dispatch.schema.json | 23 +++++++++++++++++++++ schemas/handoff.schema.json | 17 ++++++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 COMPATIBILITY.md create mode 100644 schemas/activity-entry.schema.json create mode 100644 schemas/completion.schema.json create mode 100644 schemas/dispatch.schema.json create mode 100644 schemas/handoff.schema.json diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md new file mode 100644 index 00000000..27456be7 --- /dev/null +++ b/COMPATIBILITY.md @@ -0,0 +1,32 @@ +# Schema Compatibility + +gstack uses JSON Schema definitions in `schemas/` for cross-runtime file formats +(dispatch, completion, handoff, activity). Both the gstack dispatch daemon and +OpenClaw bridge skill validate against these schemas. + +## Version policy + +Schema version is tied to gstack's `VERSION` file. + +### Breaking change (major version bump required) + +A change is **breaking** if it: +- Removes a required field +- Changes a field's type (e.g., string to integer) +- Changes the semantics of an existing field (e.g., `duration` from milliseconds to seconds) +- Narrows an enum (removes a valid value) + +### Non-breaking change (minor version bump) + +A change is **non-breaking** if it: +- Adds a new optional field +- Widens an enum (adds a new valid value) +- Relaxes a constraint (e.g., increases maxLength) +- Adds a new schema file + +### Rules + +1. **Additive by default.** New fields are always optional. Existing readers ignore unknown fields. +2. **Breaking changes require a major version bump** and must be called out in CHANGELOG. +3. **Both sides validate.** The gstack daemon validates incoming dispatch files. The OpenClaw bridge validates completion reports. If validation fails, the file is rejected with an error status, not silently ignored. +4. **Schema version in file.** Future versions may add a `schema_version` field to each file for explicit version negotiation. For now, schema version is implied by gstack VERSION. diff --git a/schemas/activity-entry.schema.json b/schemas/activity-entry.schema.json new file mode 100644 index 00000000..cf3cd27f --- /dev/null +++ b/schemas/activity-entry.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/garrytan/gstack/schemas/activity-entry.schema.json", + "title": "gstack Activity Entry", + "description": "Single entry in the weekly activity index (activity-YYYY-WNN.jsonl)", + "type": "object", + "required": ["ts", "project", "skill", "duration"], + "properties": { + "ts": { "type": "string", "format": "date-time" }, + "project": { "type": "string" }, + "skill": { "type": "string" }, + "artifacts": { "type": "array", "items": { "type": "string" }, "description": "References to produced artifacts (e.g., pr:47, review-log.jsonl, learnings:3)" }, + "duration": { "type": "integer", "description": "Session duration in seconds" }, + "dispatch": { "type": "string", "description": "Orchestrator identity if this was a dispatched session" } + }, + "additionalProperties": false +} diff --git a/schemas/completion.schema.json b/schemas/completion.schema.json new file mode 100644 index 00000000..53abca03 --- /dev/null +++ b/schemas/completion.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/garrytan/gstack/schemas/completion.schema.json", + "title": "gstack Dispatch Completion", + "description": "Completion report from a gstack coding session back to the orchestrator", + "type": "object", + "required": ["dispatch_id", "status", "duration"], + "properties": { + "dispatch_id": { "type": "string", "pattern": "^dispatch-" }, + "status": { "enum": ["completed", "failed", "cancelled", "timeout"] }, + "duration": { "type": "integer", "description": "Session duration in seconds" }, + "commits": { "type": "integer", "minimum": 0 }, + "pr": { "type": "string", "description": "PR reference in owner/repo#number format" }, + "error": { "type": "string", "description": "Error description, present only on failure/timeout" }, + "retry_count": { "type": "integer", "minimum": 0, "description": "Number of retries attempted" }, + "target_agent": { "enum": ["claude", "codex", "cursor", "gemini"] } + }, + "additionalProperties": false +} diff --git a/schemas/dispatch.schema.json b/schemas/dispatch.schema.json new file mode 100644 index 00000000..8b372aff --- /dev/null +++ b/schemas/dispatch.schema.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/garrytan/gstack/schemas/dispatch.schema.json", + "title": "gstack Dispatch", + "description": "Task dispatch from an orchestrator (e.g., OpenClaw/Wintermute) to gstack coding agent", + "type": "object", + "required": ["id", "dispatched_by", "task", "project", "project_dir"], + "properties": { + "id": { "type": "string", "pattern": "^dispatch-" }, + "dispatched_by": { "type": "string" }, + "task": { "type": "string", "minLength": 1 }, + "project": { "type": "string" }, + "project_dir": { "type": "string" }, + "target_agent": { "enum": ["claude", "codex", "cursor", "gemini"], "default": "claude" }, + "learnings": { "type": "array", "items": { "type": "string" } }, + "constraints": { "type": "array", "items": { "type": "string" } }, + "callback_url": { "type": "string", "format": "uri" }, + "clawvisor_task_id": { "type": "string" }, + "source_signature": { "type": "string", "description": "HMAC-SHA256 of task field with local secret" }, + "ttl_seconds": { "type": "integer", "minimum": 60, "maximum": 86400 } + }, + "additionalProperties": false +} diff --git a/schemas/handoff.schema.json b/schemas/handoff.schema.json new file mode 100644 index 00000000..805338dd --- /dev/null +++ b/schemas/handoff.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/garrytan/gstack/schemas/handoff.schema.json", + "title": "gstack Session Handoff", + "description": "Cross-runtime context transfer between gstack and an orchestrator (e.g., OpenClaw)", + "type": "object", + "required": ["from", "to", "project", "timestamp"], + "properties": { + "from": { "enum": ["gstack", "wintermute", "openclaw"] }, + "to": { "enum": ["gstack", "wintermute", "openclaw"] }, + "project": { "type": "string" }, + "branch": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" }, + "resume_prompt": { "type": "string", "maxLength": 4096, "description": "Exact prompt to resume session in the target runtime" } + }, + "additionalProperties": false +}