Skip to content

palm.yaml and palm.lock.yaml

A project that uses palm commits three things: palm.yaml, palm.lock.yaml and the files palm generated. palm.yaml says what you want. The lock says what that resolved to and what palm wrote. git carries the files to every clone and every CI run.

~/project
$ cat palm.lock.yaml
# palm lockfile: written by palm, do not edit by hand.
version: 3
sources:
mattpocock/skills:
url: https://github.com/mattpocock/skills.git
ref: ^1.2
resolved: v1.2.3
sha: 2c0db9046017ad90f70ed14046665fee10b122b9
descriptor: marketplace
entries:
- kind: skill
name: tdd
source: mattpocock/skills
path: skills/engineering/tdd
content: sha256:e751ad1a5bc7e0600cfc571ddb396bcec66547a02c03ce1d3b8b5bc6d5a2069e
render: {claude: sha256:04476c5e20ef4f78bac75223193a01ac7248dfc993dce7d085160e577436dec8, codex: sha256:c3bb4f3b844914d883e9628da120269fb927a582796e4dce255fc07d4e01d1f7}
files:
- .agents/skills/tdd/SKILL.md
- .agents/skills/tdd/agents/openai.yaml
- .agents/skills/tdd/references/mocking.md
- .claude/skills/tdd/SKILL.md
- .claude/skills/tdd/references/mocking.md
notes:
- claude gets its own copy in .claude/skills
$ palm install
1 unchanged.
$ palm check
✓ manifest and lock agree
✓ palm.yaml targets match the lock
✓ every entry is rendered
✓ every entry is on all its targets
✓ generated files match the lock
✓ no stray file in a folder palm owns
- applied files outside the lock: skipped (global scope only)
✓ in-repo sources match the lock
✓ no generated path lies inside a source
✓ every program palm installed is trusted
✓ no foreign command in the hook files palm manages
✓ no foreign stdio server in the MCP files palm manages
✓ every hook script exists
✓ no literal secret in generated files
✓ every variable the servers need is set
✓ generated files are committed
✓ every source is declared
✓ no link leaves the scope
✓ no hidden Unicode in generated files
✓ no harness loads an entity twice
✓ no two agents or commands share a name in a harness folder
✓ every skill an agent preloads is installed
✓ instruction files are within harness limits
no problems

The lock pins the source to a commit and records, per entry, one content hash, one render hash per harness and the file list. A bare palm install on a current checkout writes nothing, and palm check confirms that every file matches.

File Holds Written by Commit it
palm.yaml targets; sources with ref:, root: and layout:; entries with filters; hand-declared MCP servers palm init, palm install, palm remove, palm update --to, your editor yes
palm.lock.yaml per source the URL, range, resolved tag, commit and layout; per entry the content hash, one render hash per target, the files, merged-entry identities, executable hashes, trust and notes palm install, palm remove, palm update yes
generated files every harness file palm wrote, and .palm/assets/ palm install, palm update, palm remove yes

.palm/local/ and palm.local.yaml are the only palm paths git ignores. They are reserved for personal additions in palm 0.3.

Every generated file is a function of palm.yaml, the lock and the sources. palm could rebuild them on each machine, but committing them has three payoffs.

  • A clone works without palm. Harnesses read committed files, and hook scripts sit in .palm/assets/, so nothing points at a missing path.
  • The diff is the review. An update shows the changed skill text, the new hook command and the moved lock hash in one pull request.
  • git is the record of the machine. palm keeps no state file in the project, and a teammate’s git pull delivers a removal as a deleted file.

.palm/assets/ holds scripts a reviewer should read, so palm does not mark it as generated for GitHub’s diff view.

palm install with no arguments makes the disk match palm.yaml and the lock. It compares three things for each entry: what palm.yaml asks for, what the lock records, and what is on disk.

Situation What palm does Status
In palm.yaml, not in the lock resolves, renders, writes, adds it to the lock + installed
In the lock, not in palm.yaml deletes its files and merged entries, drops it from the lock - removed
In both, files match the render nothing one unchanged count line for all of them
A generated file is missing writes it again from the locked commit ↺ restored
A generated file differs from the render keeps your file, exits 1 ! modified (kept)
The render changed, such as an edited in-repo source writes the new render, updates the lock ~ re-rendered
ref: in palm.yaml differs from the lock prints the update plan, asks for consent when a program changes, then resolves the new ref ~ updated

A bare install needs no network when the cache holds every locked commit. On a clean clone with a current lock, it writes neither palm.yaml nor the lock. --dry-run prints would install, would restore and would remove lines and writes nothing.

A run that installs nothing writes nothing. When no entity installs, because it failed, was refused or was declined, palm.yaml and the lock keep their bytes.

The lock stores one render hash per entry and target, computed from the files and merged entries palm would write. Every machine computes the same hash from the same lock, so a teammate’s pull that moves the lock is an upgrade, never “your edits”.

A file that differs from the render is yours. palm never overwrites it or deletes it without --force, and the message names the command that does.

! kept .claude/skills/tdd/SKILL.md: you changed it since palm wrote it (palm remove mattpocock/skills skill:tdd --force)

The lock holds no absolute path, home directory, hostname, timestamp, per-file hash or secret value. A newcomer’s lock for the same palm.yaml is byte for byte the same as a veteran’s. Entries are sorted by kind and name, and saving the same lock twice gives the same bytes.

Merged entries, such as a hook inside .claude/settings.json, carry an identity independent of their value. An edited hook is “changed”, not “missing”, and removing one plugin can never take another plugin’s hook.

palm writes palm.yaml with targets: first, and switches a list longer than three entries to block style, one entry per line.