Filed here because dotfiles is the closest existing home, not because this
belongs here long term. The payload is documents, not scripts, and the whole
point is that it must not inherit this repo's script-testing load. Move it
once a destination exists.
The problem
personify's VOICE.md is gitignored on purpose, like a .env. It is a
detailed per-user writing profile, roughly 24KB, and it is loaded by the skill
as authoritative. Because it is gitignored, it reaches other machines through
no channel at all: neither git pull nor /plugin update moves it, and
nothing reconciles two machines that disagree.
It is not alone. VOICE.corpus.md is gitignored beside it, and the same shape
shows up across the personal and side-gig environments: a document that has to
land at a known path on several machines, is not secret enough for a secret
store, and is not a script.
The failure mode that makes it worth fixing
Silent, not loud. SKILL.md Step 0 takes the first path that exists, in this
order:
$PERSONIFY_VOICE
$XDG_CONFIG_HOME/personify/VOICE.md, else ~/.config/personify/VOICE.md
VOICE.md in the skill's own directory
A real file at 2 shadows the checkout at 3. You then edit the checkout copy,
the skill keeps loading the config copy, and the output is wrong in a way
nothing reports. SKILL.md line 20 already tells the model to warn when it
sees a guide at both paths, so the sharp edge is known, but that only fires
when both exist and the model is already in Step 0.
Drift is not hypothetical. This machine carries
~/.config/personify/VOICE.md.bak-20260814 at 10,767 bytes against a current
23,920. Less than half the current guide.
This machine is currently fine, and that is luck of local setup rather than
anything the repo guarantees: ~/.config/personify/VOICE.md is a symlink to
~/Developer/personify/VOICE.md, so both paths hash identically
(9ec05054). Nothing in either repo creates that symlink.
Two separable pieces
1. Drift visibility. Cheap, useful under every transport, and worth doing
first.
VOICE.md already carries a hand-maintained **Status:** v0.3 marker on line
3, which will drift from reality the first time it is edited without a bump. A
content hash cannot drift.
Two designs, answering different questions:
- Report-only fingerprint. Step 0 prints the loaded file's short hash next to
the version it already reads. No stored expected value, nothing to bless,
nothing to go stale. Comparing two machines is then reading two lines. This
answers "are these the same guide", which is the question actually being
asked.
- Stored expected hash. Also catches corruption and forgotten edits on a
single machine, but needs re-blessing on every voice edit. A stale expected
hash is worse than none, because it cries wolf. If this is wanted, the
expected value belongs beside the file in the private repo, so re-blessing is
a commit to the repo just edited rather than a cross-repo chore.
Preference is report-only, and that the hash replaces reliance on the
hand-maintained Status: line rather than sitting beside it. A SHA-256 of a
document discloses nothing about its contents, so the value itself is safe to
print or publish either way.
2. Distribution. A content-distribution repo, private, on the
twistedmelonman account. Deliberately an exception to the public-by-default
rule for that account, because the payload is personal behavioral profiles.
Minimum shape:
- A manifest of source path to destination path.
- An installer that symlinks each entry.
- CI that runs link-check and markdown-lint, and nothing else. Seconds, not
minutes.
The constraint that keeps it small
Write it into that repo's own CLAUDE.md as a rule with its reason: no
executable payload, the only script is the installer, CI runs link-check and
markdown-lint only. Without that written down, the first thing that almost fits
gets added, and the testing load arrives with it. That is how this repo got to
where it is, and is the reason the new thing should not live here.
The part that needs actual care
The installer can create a symlink on a clean machine. It cannot safely fix a
machine that already has a real file at the destination, which is exactly the
silently-shadowing case. It should refuse loudly and name both paths rather
than clobber or back up and continue. The machine in that state is the one
currently producing wrong output, so a quiet fix there is the worst outcome.
Open questions
- How many documents are in this class across all the environments? If it is
genuinely one file, a private gist symlinked into place is competitive and
needs no infrastructure. The answer decides repo versus gist, and cannot be
answered from this machine.
- How many machines have
personify installed? Fewer than three weakens the
ergonomics argument for a repo considerably.
- Does
VOICE.corpus.md need the same treatment, or is provenance-only
material fine living in one place? It is not loaded when writing.
Not doing now
No changes made. Recorded so the design is not re-derived later.
Filed here because dotfiles is the closest existing home, not because this
belongs here long term. The payload is documents, not scripts, and the whole
point is that it must not inherit this repo's script-testing load. Move it
once a destination exists.
The problem
personify'sVOICE.mdis gitignored on purpose, like a.env. It is adetailed per-user writing profile, roughly 24KB, and it is loaded by the skill
as authoritative. Because it is gitignored, it reaches other machines through
no channel at all: neither
git pullnor/plugin updatemoves it, andnothing reconciles two machines that disagree.
It is not alone.
VOICE.corpus.mdis gitignored beside it, and the same shapeshows up across the personal and side-gig environments: a document that has to
land at a known path on several machines, is not secret enough for a secret
store, and is not a script.
The failure mode that makes it worth fixing
Silent, not loud.
SKILL.mdStep 0 takes the first path that exists, in thisorder:
$PERSONIFY_VOICE$XDG_CONFIG_HOME/personify/VOICE.md, else~/.config/personify/VOICE.mdVOICE.mdin the skill's own directoryA real file at 2 shadows the checkout at 3. You then edit the checkout copy,
the skill keeps loading the config copy, and the output is wrong in a way
nothing reports.
SKILL.mdline 20 already tells the model to warn when itsees a guide at both paths, so the sharp edge is known, but that only fires
when both exist and the model is already in Step 0.
Drift is not hypothetical. This machine carries
~/.config/personify/VOICE.md.bak-20260814at 10,767 bytes against a current23,920. Less than half the current guide.
This machine is currently fine, and that is luck of local setup rather than
anything the repo guarantees:
~/.config/personify/VOICE.mdis a symlink to~/Developer/personify/VOICE.md, so both paths hash identically(
9ec05054). Nothing in either repo creates that symlink.Two separable pieces
1. Drift visibility. Cheap, useful under every transport, and worth doing
first.
VOICE.mdalready carries a hand-maintained**Status:** v0.3marker on line3, which will drift from reality the first time it is edited without a bump. A
content hash cannot drift.
Two designs, answering different questions:
the version it already reads. No stored expected value, nothing to bless,
nothing to go stale. Comparing two machines is then reading two lines. This
answers "are these the same guide", which is the question actually being
asked.
single machine, but needs re-blessing on every voice edit. A stale expected
hash is worse than none, because it cries wolf. If this is wanted, the
expected value belongs beside the file in the private repo, so re-blessing is
a commit to the repo just edited rather than a cross-repo chore.
Preference is report-only, and that the hash replaces reliance on the
hand-maintained
Status:line rather than sitting beside it. A SHA-256 of adocument discloses nothing about its contents, so the value itself is safe to
print or publish either way.
2. Distribution. A content-distribution repo, private, on the
twistedmelonmanaccount. Deliberately an exception to the public-by-defaultrule for that account, because the payload is personal behavioral profiles.
Minimum shape:
minutes.
The constraint that keeps it small
Write it into that repo's own
CLAUDE.mdas a rule with its reason: noexecutable payload, the only script is the installer, CI runs link-check and
markdown-lint only. Without that written down, the first thing that almost fits
gets added, and the testing load arrives with it. That is how this repo got to
where it is, and is the reason the new thing should not live here.
The part that needs actual care
The installer can create a symlink on a clean machine. It cannot safely fix a
machine that already has a real file at the destination, which is exactly the
silently-shadowing case. It should refuse loudly and name both paths rather
than clobber or back up and continue. The machine in that state is the one
currently producing wrong output, so a quiet fix there is the worst outcome.
Open questions
genuinely one file, a private gist symlinked into place is competitive and
needs no infrastructure. The answer decides repo versus gist, and cannot be
answered from this machine.
personifyinstalled? Fewer than three weakens theergonomics argument for a repo considerably.
VOICE.corpus.mdneed the same treatment, or is provenance-onlymaterial fine living in one place? It is not loaded when writing.
Not doing now
No changes made. Recorded so the design is not re-derived later.