makepad/docs/agents/change-report.schema.json
Admin 1c6549449f change_report: the change report format, its checker and the agents' steps for "send what I changed" -- people who install Makepad apps with the Builder change them with their own coding agent (Codex desktop or CLI, Claude Code, ...); a change report tells Makepad what they changed as concepts (what is different for someone using the app, and why), read and approved by them before it is sent. docs/agents/change-report.md is the format: a zip of at most 256 KB holding exactly report.json (format makepad-change-report, version 1: app id and release, optional base commits and summary, 1-100 changes with title, kind fix/feature/tweak/ui/performance/refactor/docs/other, what, why, areas and an optional tiny snippet, the redactions by kind and count, and whether a diff is included), REPORT.md (the same list for people, naming every change) and, only on opt-in, a trimmed changes.diff; UTF-8 text only, per-file limits, no binary patches, unknown fields refused. Its JSON schema is docs/agents/change-report.schema.json, with a complete example. The anonymisation rules say what an agent replaces with which bracketed placeholder ([name], [email], [path], [host], [secret], [private-url], [personal]) and lists. libs/change_report (makepad-change-report: strict-json, zip-file and fast-inflate only) checks a report exactly as documented, in the Builder before sending and on the server when it arrives: sizes before inflating (output bounded by the declared sizes, CRC checked), the schema, REPORT.md against the titles, the diff flag, and a safety-net scan for email addresses (not @example.*, @makepad.nl or git@), absolute and home paths (Unix, drive letters, shares), well-known key and token formats, Bearer tokens, quoted secret assignments, credentials in URLs and private network addresses, naming file, line and kind of each finding. The root AGENTS.md (which ships in every source pack) gets "Sending your changes to Makepad": find the installation, collect the changes with the Builder's changes command (or git), write the concept list, anonymise, show it and wait for approval (diff and reply email opt-in), write builder/changes/<app>-report, check it with send-changes, send with --yes (or leave it for the Builder's review, or POST the zip with curl), report the id. Tests: the documented example round-trips and opens, the schema file matches the checker's limits and kinds, every schema rule, zip contents and size limits (whole zip, a file's unpacked size, extra files, binary text, missing files, diff flag, binary patch), the anonymisation checklist (19 kinds of personal or secret data found, 16 look-alikes left alone), a refused report lists every finding, folder packing. Checked on the Linux node: cargo test --release -p makepad-change-report, 8 passed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:13:13 +02:00

89 lines
3.9 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://makepad.nl/schemas/change-report-1.json",
"title": "Makepad change report (report.json)",
"description": "What someone changed in a Makepad app, told as concepts. See docs/agents/change-report.md. libs/change_report checks exactly these rules (plus the anonymisation scan) before a report is sent and when it is received.",
"type": "object",
"additionalProperties": false,
"required": ["format", "version", "app", "changes", "redactions", "diff"],
"properties": {
"format": {"const": "makepad-change-report"},
"version": {"const": 1},
"app": {
"type": "object",
"additionalProperties": false,
"required": ["id", "release"],
"properties": {
"id": {"$ref": "#/$defs/identifier", "description": "The app's id in the Builder (scope, stage, calculator, ...)."},
"release": {"$ref": "#/$defs/identifier", "description": "The release the edits were made against: \"release\" in builder/installed/<app>.json."}
}
},
"base": {
"type": "array",
"maxItems": 8,
"description": "The commits of that release's repositories, from the same file.",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["repository", "commit"],
"properties": {
"repository": {"$ref": "#/$defs/identifier"},
"commit": {"type": "string", "pattern": "^[0-9a-fA-F]{7,64}$"}
}
}
},
"summary": {"$ref": "#/$defs/text", "maxLength": 2000},
"changes": {
"type": "array",
"minItems": 1,
"maxItems": 100,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["title", "kind", "what"],
"properties": {
"title": {"$ref": "#/$defs/line", "minLength": 1, "maxLength": 120},
"kind": {"enum": ["fix", "feature", "tweak", "ui", "performance", "refactor", "docs", "other"]},
"what": {"$ref": "#/$defs/text", "minLength": 1, "maxLength": 2000, "description": "What changed, as the person using the app sees it. Concepts, not code."},
"why": {"$ref": "#/$defs/text", "maxLength": 1000, "description": "Why it was changed."},
"areas": {
"type": "array",
"maxItems": 16,
"description": "Screens, components or modules it touches, by name (repo-relative paths are fine).",
"items": {"$ref": "#/$defs/line", "minLength": 1, "maxLength": 80}
},
"snippet": {
"type": "object",
"additionalProperties": false,
"required": ["text"],
"description": "Only when the change cannot be told without it.",
"properties": {
"language": {"type": "string", "maxLength": 32},
"text": {"$ref": "#/$defs/text", "minLength": 1, "maxLength": 1500, "description": "At most 40 lines."}
}
}
}
}
},
"redactions": {
"type": "array",
"maxItems": 50,
"description": "What the agent took out, by kind (never the removed value itself). Empty when nothing was.",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["what", "count"],
"properties": {
"what": {"$ref": "#/$defs/line", "minLength": 1, "maxLength": 120},
"count": {"type": "integer", "minimum": 1, "maximum": 100000}
}
}
},
"diff": {"type": "boolean", "description": "True exactly when the zip holds changes.diff (only when the person chose to include it)."}
},
"$defs": {
"identifier": {"type": "string", "pattern": "^[A-Za-z0-9_-][A-Za-z0-9._-]{0,127}$"},
"text": {"type": "string", "pattern": "^[^\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f]*$"},
"line": {"type": "string", "pattern": "^[^\\u0000-\\u0008\\u000a-\\u001f\\u007f-\\u009f]*$"}
}
}