cargo-makepad, makepad-builder (with an Android step on every OS), CI tools and tiles, and the remaining libraries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
8.9 KiB
Change reports: sending your changes to Makepad
People who install Makepad apps with the Makepad Builder change the apps' source with their own coding agent (Codex desktop, Codex CLI, Claude Code or any other). A change report tells Makepad what they changed, as concepts: what is different for someone using the app, and why. The person reads the report before it is sent; nothing leaves their machine without that.
This document is the format and the rules. AGENTS.md ("Sending your
changes to Makepad") tells an agent how to write one; libs/change_report
checks one exactly as described here, in the Builder before sending and on
the server when it arrives. The JSON schema of report.json is
change-report.schema.json; a complete example
is change-report-example/report.json.
The zip
A change report is one zip file of at most 256 KB holding exactly:
| file | ||
|---|---|---|
report.json |
required | the list of changes (schema below), at most 128 KB |
REPORT.md |
required | the same list for people, at most 128 KB; this is what the person reads before sending |
changes.diff |
only if the person opted in | a trimmed unified diff of the changes that matter, at most 1 MB unpacked |
No other files, no folders, no binaries: each file is UTF-8 text without NUL
or other control characters (tabs and newlines are fine). A diff holds no
GIT binary patch; leave images and other binary files out.
The Builder can make the zip itself: an agent may leave the three files in
a folder, builder/changes/<app>-report/, instead of a zip at
builder/changes/<app>-report.zip.
report.json
{
"format": "makepad-change-report",
"version": 1,
"app": {"id": "calculator", "release": "2026-09-20"},
"base": [{"repository": "makepad", "commit": "7a533b61ee0c4e1f9d2b3a4c5d6e7f8091a2b3c4"}],
"summary": "Two small changes so the calculator suits a keyboard-first workflow.",
"changes": [
{
"title": "Enter repeats the last operation",
"kind": "feature",
"what": "Pressing Enter again after a result applies the same operation and operand once more.",
"why": "Adding the same amount several times took retyping the operand each time.",
"areas": ["calculator keypad", "apps/calculator/src/app.rs"]
},
{
"title": "Larger result text",
"kind": "ui",
"what": "The result line uses a larger font and stays right-aligned when the window is narrow.",
"why": "The result was hard to read from a distance.",
"areas": ["result display"],
"snippet": {"language": "splash", "text": "draw_text +: {text_style: theme.font_regular{font_size: 28}}"}
}
],
"redactions": [
{"what": "home folder path", "count": 2},
{"what": "personal name in a comment", "count": 1}
],
"diff": false
}
| field | ||
|---|---|---|
format, version |
required | "makepad-change-report" and 1 |
app.id |
required | the app's id in the Builder (scope, stage, calculator, ...) |
app.release |
required | the release the edits were made against: "release" in builder/installed/<app>.json |
base |
optional | that release's repositories and commits ("repositories" in the same file), at most 8 |
summary |
optional | one paragraph, at most 2000 characters |
changes |
required | 1 to 100 items |
changes[].title |
required | one line, at most 120 characters |
changes[].kind |
required | fix, feature, tweak, ui, performance, refactor, docs or other |
changes[].what |
required | what changed, as the person using the app sees it; concepts, not code; at most 2000 characters |
changes[].why |
optional | why, at most 1000 characters |
changes[].areas |
optional | up to 16 screens, components or modules by name (repo-relative paths are fine), 80 characters each |
changes[].snippet |
optional | {"language", "text"}, only when the change cannot be told without it; at most 1500 characters and 40 lines |
redactions |
required | what was taken out, by kind and count, never the removed value; [] when nothing was |
diff |
required | true exactly when the zip holds changes.diff |
Text fields hold no control characters other than newlines and tabs; titles and areas are one line. Unknown fields are refused, so a report carries nothing the person did not see listed.
REPORT.md
The same changes, for people. It must name every change by its exact title; otherwise its form is free. The Builder's own rendering, which agents may copy:
# Changes to calculator (release 2026-09-20)
Two small changes so the calculator suits a keyboard-first workflow.
## 1. Enter repeats the last operation (feature)
Pressing Enter again after a result applies the same operation and operand once more.
Why: Adding the same amount several times took retyping the operand each time.
Areas: calculator keypad, apps/calculator/src/app.rs
## Taken out before sending
- home folder path × 2
- personal name in a comment × 1
No code diff is included.
Anonymisation
The agent scans everything that goes into the zip (the list, REPORT.md and
the diff) and replaces personal and secret data with a bracketed
placeholder, then lists each kind it replaced in redactions:
| take out | replace with |
|---|---|
| names of people, usernames, handles | [name] |
| email addresses | [email] |
absolute paths and home folders (/Users/…, /home/…, C:\Users\…, \\server\share) |
a repo-relative path, or [path] |
host and machine names, private IP addresses (10.x, 192.168.x, 172.16-31.x, .local, .internal, .lan) |
[host] |
| API keys, tokens, passwords, private keys, credentials in URLs | [secret] |
| URLs of private or internal services | [private-url] |
| personal data in strings and comments (addresses, phone numbers, customer data) | [personal] |
Public URLs (makepad.nl, github.com/makepad, documentation) and placeholder
addresses (@example.com) may stay. Use these bracketed placeholders, not
<angle brackets>, so every report reads the same.
The Builder and the server run a safety-net scan of every file and refuse a
report that still holds an email address (other than @example.* and
@makepad.nl), an absolute or home folder path, a well-known key or token
format (sk-…, ghp_…, AKIA…, xoxb-…, private key blocks, Bearer …,
password = "…"-style assignments), credentials in a URL, or a private
network address. The refusal names the file, line and kind of each finding.
Names of people and personal details in prose cannot be found this way:
they are the agent's to take out and the person's to check when reading.
Sending
The person sends the report from the Builder (e on the app's row: the
review page lists the changes, then Send anonymously, Send with my email,
Read it all or Don't send), or the agent sends it after the person approved
the list in the conversation:
makepad-builder.exe send-changes <app> [PATH] [--yes] [--with-email] (Windows)
./makepad-builder.sh send-changes <app> [PATH] [--yes] [--with-email] (macOS, Linux)
Without --yes it only checks the report and prints what would be sent.
--with-email includes the address the Builder is logged in with, so
Makepad can reply; the agent never sees it. A sent report is renamed
<app>-report-sent-<id> in builder/changes/.
makepad-builder.exe changes <app> / ./makepad-builder.sh changes <app> prints the
edits to describe: the diff of the app's source snapshot against the
release it was checked out at (--files lists the files).
The endpoint
POST https://makepad.nl/api/feedback/changes
X-Makepad-Feedback: 1
Content-Type: application/zip
X-Makepad-Reply-To: name@example.com (optional)
<the zip>
MAKEPAD_FEEDBACK_URL, when set, names the feedback endpoint instead
(http://127.0.0.1:8080/api/feedback); change reports then go to that URL
followed by /changes.
| answer | |
|---|---|
200 {"ok":true,"id":N} |
stored; N is the report id |
400 {"error":…} |
invalid X-Makepad-Reply-To |
403 |
no X-Makepad-Feedback: 1, or a browser Origin |
413 |
larger than 256 KB |
415 |
not application/zip, or not a zip |
422 {"error":…} |
the report fails a check; the reason says which (scan findings one per line) |
429 |
rate limit (shared with app feedback: 10 per client address and 300 overall per hour) |
507 |
feedback storage full |
Without the Builder (a plain HTTP fallback):
curl -sS -X POST -H 'X-Makepad-Feedback: 1' -H 'Content-Type: application/zip' \
--data-binary @changes.zip https://makepad.nl/api/feedback/changes
The server keeps the zip as sent and REPORT.md as the text of a feedback item in the admin Feedback inbox (source "changes"), with the release, the change titles, what was taken out and whether a diff is included. No IP address or other request data is stored.