Repository navigation
Emulo v0.6.6: the mine-then-install path works end to end
Fixed
- The documented mining path stopped at the install step.
RUN_ME.mdasks your agent to writeemulo-out/you.mdand then printsemulo --install emulo-out/you.md --target claudefirst. Theclaudeandcodextargets install the profile as a skill, which needsnameanddescriptionfrontmatter, andRUN_ME.mdnever asked for any, so ayou.mdwritten exactly as instructed was refused with "profile must start with exact name and description frontmatter fields". Found by following the documented flow end to end while writing a user guide.--installnow addsname: youand a default description to the installed copy when the file has no frontmatter, and prints a note saying so. Youryou.mdis not modified. A file whose frontmatter already hasnameanddescriptionis installed as written, whatever the name says. A frontmatter block that is not closed, lacksnameordescription, has a line that is notkey: value, leaves a value empty or repeats a key is still refused, now with a line that says how to fix it. The fix sits in the installer rather than inRUN_ME.mdbecause every other target strips frontmatter anyway, so asking the agent for YAML it only needs for two targets would add a step it can get wrong, and ayou.mdwritten from an olderRUN_ME.mdinstalls too. A UTF-8 byte order mark, blank lines, or an indent before the opening---no longer hide the file's own frontmatter, so such a file is installed with its own block instead of having the default stacked on top. The same blank line had also let a profile's frontmatter leak intoAGENTS.md,GEMINI.md, the OpenCode rules file and the Cursor rule in earlier releases; it is now stripped there too. - Some secrets passed through redaction. Anthropic keys (
sk-ant-...), Google API keys (AIzaplus 35 characters) and the password in a connection URL (postgres://with auser:secretpair before the host, and the same for mysql, mongodb, redis, amqp, https or any other scheme) were written to the corpus and chunks as typed. A password on a host with a dot was only removed by accident, because the email rule tookpassword@host.tldand the host with it; onlocalhostit survived. All three are now redacted. The URL rule keeps the user and host and runs to the last@before a space,/,?or#, so a rawp@ssis covered whilehttp://localhost:3000?next=a@b.comstays a port and a query. The same check found that OpenAI project, service account and admin keys (sk-proj-...,sk-svcacct-...,sk-admin-...) also passed, because the plainsk-rule stops at their first dash; those are redacted too. The Anthropic and OpenAI project rules only match at the start of a word, so a kebab-case branch or tool name that contains a prefix mid-word is left alone. A review before release found that shape ten times in one real history. - The browser scan at
/scanhad the same gaps. It runs its own port of the redaction table, and it now has the same rules in the same order. /scanused a regular expression feature that Safari before 16.4 cannot parse. Its phone rule, live since at least 0.6.4, used a lookbehind, and one pattern the browser cannot parse stops the whole script, so the scan would not load there at all. Found by a review that required no lookbehind in the page. The phone rule and the new rules now keep that boundary another way, with output identical to Python's, and a test fails if the page contains a lookbehind. Not checked on a real Safari older than 16.4.- The redaction count did not say what it counted.
secrets/PII redacted: 2read as two items, but it is the number of messages that had anything redacted. The line is nowmessages with secrets/PII redacted: 2, and the stat on/scanreads "messages redacted". - An empty
CODEX_HOMEorXDG_DATA_HOMEsearched the current directory. Set but empty, either one made discovery look in relativesessions,archived_sessionsandopencodefolders under wherever you ranemulo, and mine whatever it found there. Empty now means unset. --cardwithout a card pointed at a flow that no longer exists. It said to pasteMINING_PROMPT.mdinto your agent. TheRUN_ME.mdflow writesyou.mdand no card; only the agent mining flow (run emulothrough the bootstrap, oremulo:mine) writescard.json, into the active profile. The message now says that, and namesemulo plugin statusto find the card andemulo --card <card_path>to render it.- Docs that disagreed with the code.
SECURITY.mdlisted three sources where Emulo reads five (OpenCode and Antigravity were missing) and said redaction covers "common API keys"; it now names every pattern, each one tested, and a case it misses. The README said only.jsonlmessages are mined (OpenCode is SQLite and JSON), thatemulo --dry-runprints the read-only plan (it prints counts; the JSON plan comes fromemulo plugin preflighton the agent path, and plainemulowrites chunks with no approval step because it makes no model calls), that--cardworks after mining (not after theRUN_ME.mdpath), and that the MCP server serves the profile you mined locally (it serves the profile activated by the agent mining flow, so ayou.mdinstalled fromRUN_ME.mdis invisible to it). The--pathhelp said.jsonlonly. The MCP tool description named work, design and write but not video, which the tool has accepted since 0.4.0.site/llms.txtand the working profile guide at/agent-profilenamed 0.6.2 as the published CLI. The guide also told plain CLI users to review a mining plan that path does not have and said the MCP server serves any local profile, and the home page said theRUN_ME.mdstep shows the plan and cost before it starts. All three now describe the two paths the way the README does.
Verified
- The documented path, run with the installed
emulocommand in a clean home directory:emulo --source codex --chunks 1on a history holding an Anthropic key, a Google API key and a Postgres URL with a password wrote one chunk with[ANTHROPIC_KEY],[GOOGLE_API_KEY]andpostgres://app:[REDACTED]@localhost:5432/app_dev, and none of the three secrets appears in any output file. Ayou.mdwritten exactly as the generatedRUN_ME.mdspecifies, with no frontmatter, installed with all three commandsRUN_ME.mdprints and with the other three targets it lists (agents,gemini,opencode): six exit codes of 0.emulo verifytraced both of its quotes. The same run hadCODEX_HOMEandXDG_DATA_HOMEset to empty and asessions/folder in the working directory, and nothing from that folder was read. - 12 new Python tests. Ten failed before the fix: the full
RUN_ME.mdpath (mine a fixture history with the real CLI, writeyou.mdas instructed, run every install command it prints plus the other listed targets, all must succeed), the note printed when frontmatter is added, a BOM before valid frontmatter, frontmatter after blank lines, whitespace-only lines or an indented marker for both skill targets, the same file installed toAGENTS.mdwithout its frontmatter, Anthropic, Google and OpenAI project keys with prose that must survive ("the AIza prefix marks a Google key"), connection URLs across schemes with URLs that must be left alone (no password, a port, a path with a colon), the count label on a message holding three redacted items, emptyCODEX_HOMEandXDG_DATA_HOMEthrough both the module and the real CLI, and the MCP description naming every domain its enum accepts. Two are guards that passed before and are not evidence for a change:emulo plugin statusanswering with no profile, and a timing test that the URL rule scans a 200,000-character token once. Three existing tests changed: the--cardhint test now also requires the new hint and failed before the fix, a card render test that required the oldMINING_PROMPT.mdhint now forbids it, and the dry-run test expects the new count label. tests/test_redaction_table.pyholds one realistic fake sample for every patternSECURITY.mdnames, 48 rows, each of which must come out as its documented placeholder, plus the documented miss, which must still pass through. A third test readsSECURITY.mdand fails if a pattern it names has no row or a row is not named there, and a fourth holds six kebab-case words containing a key prefix, which must come through untouched. The kebab test failed before the left boundary was added. The browser scan runs the same rows, read from the same Python table. To check that these tests hold the rules in place, each of the 17 rules was removed in turn fromemulo.pyand from/scan, five alternatives were narrowed in both (svcacct,admin,gh[pousr],xox[baprs],api[_-]?key), the left boundary was taken off each new key rule, and the?and#guard on the URL rule was removed: 50 mutations, and a test failed for every one.- 5 new site tests: no lookbehind anywhere in the page, the URL rule staying fast on a 600,000-character input, the browser redaction for the new patterns, the full
SECURITY.mdtable, and the page's own folder-picker handler, fed a stubbed directory, running a scan that renders "messages redacted". The byte-for-byte parity test against Python now also runs 23 credential samples, positive and negative. The lookbehind test, the new-pattern test and the parity test failed before the change. The timing test was checked the other way: with the URL rule swapped for a plain global pattern with no boundary, it took 131 s and failed, and with a capture-group boundary in place of the scanner, the parity test failed ona://u:p@b://v:q@h, where Python redacts both passwords and that form misses the second. The table test was written after it, so its evidence is the mutation run above rather than a failure before the fix; the same holds for the Python table test. - Full suite on Python 3.11 with the
[pro]extra: 568 tests, 5 skipped, 0 failures. On Python 3.13 withoutcryptography: 546 tests, 13 skipped, 0 failures. On Windows the symlink tests skip, and the two release pin tests skip until thev0.6.6tag exists. - Site tests after a clean
npm ci: 46 tests, 0 failures. - The
v0.6.6bootstrap runtime pinsemulo.pyto SHA-2569771458e43c74c9b(prefix);MINING_PROMPT.mdis unchanged atee22077c2cda3c1c(prefix). Full digests in.agents/skills/emulo/runtime.json. - Not fixed, found while checking the redaction docs:
AWS_SECRET_ACCESS_KEY=...is not redacted, because the assignment rule only matches a name ending inapi_key,secret,token,passwordorpasswd.SECURITY.mdnow says so. A connection URL password holding a raw#,/,?or space, or following a user name with a raw@, is not redacted; none of those is valid there unencoded, the encoded forms are redacted, andSECURITY.mdlists them with a test for each. The email rule's time grows with the square of a token's length, so one 100,000-character run of letters with no spaces takes about 7 s to redact; this release does not change it. Python and the browser scan disagree on what counts as whitespace in a pattern: Python treats the control characters U+001C to U+001F as whitespace and the browser does not, and the browser treats U+FEFF as whitespace and Python does not, so a message holding one of those characters next to a secret can redact differently in the two. - Not verified: Python 3.8, because no 3.8 interpreter is installed on the release machine, and
pip install emulo==0.6.6from PyPI, because the version is not published at the time of writing.
End to end, from a fresh install
$ emulo --source codex --chunks 1
messages with secrets/PII redacted: 2 exit 0
the eval key is [ANTHROPIC_KEY] and maps uses [GOOGLE_API_KEY]
DATABASE_URL=postgres://app:[REDACTED]@localhost:5432/app_dev fix the migration
you.md written as RUN_ME.md specifies, no frontmatter
$ emulo --install emulo-out/you.md --target claude note: adds default frontmatter exit 0
$ emulo --install emulo-out/you.md --target codex note: adds default frontmatter exit 0
$ emulo --install emulo-out/you.md --target cursor --repo . exit 0
$ emulo --install emulo-out/you.md --target agents --repo . exit 0
$ emulo --install emulo-out/you.md --target gemini --repo . exit 0
$ emulo --install emulo-out/you.md --target opencode exit 0
$ emulo verify emulo-out/you.md -> 2/2 quotes traced exit 0
On 0.6.5 the same you.md is refused at the first install command.