{"_id":"@avytheone/efmesh","_rev":"11-d9ac4c29f9a919982ffd53f2c86e0760","name":"@avytheone/efmesh","dist-tags":{"beta":"0.1.0-beta.2","latest":"0.5.0"},"versions":{"0.1.0-beta.1":{"name":"@avytheone/efmesh","version":"0.1.0-beta.1","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.1.0-beta.1","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"ae2b64177a1a72790d44c24de686e9e25019b39b","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.1.0-beta.1.tgz","fileCount":40,"integrity":"sha512-PlBSmsWwGhbVzzfhWNb3viCIY3a0G3UQn8I26XeoH1/tCryQ9WZ6ib/ilQ8cW0YqKd/qg0dkXano4GBW9RjnPg==","signatures":[{"sig":"MEUCIQDOVattV/TFSRogTdaSQqGI7McVELh3qCcGPf4XrJZ/KgIgF221troZqJVTbU+fJ9aw707jCEDkPBylTP8PIEM546c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":320211},"type":"module","module":"src/index.ts","exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"b74d5b148c578e8624903f70f1e18ee7420b3aa0","scripts":{"test":"bun test","check":"tsc --noEmit"},"_npmUser":{"name":"avytheone","email":"yakimanskiyav@yandex.ru"},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.12.1","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.15.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","@types/bun":"latest","typescript":"^7.0.2"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.1.0-beta.1_1784196879194_0.4496305284750637","host":"s3://npm-registry-packages-npm-production"}},"0.1.0-beta.2":{"name":"@avytheone/efmesh","version":"0.1.0-beta.2","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.1.0-beta.2","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"7a5a88626fe7bcc8a3cb25921e141b1aa1ed8186","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.1.0-beta.2.tgz","fileCount":40,"integrity":"sha512-8FQcP9pEECaYM7SefXALWQJC2KlMFmsUgcmo+lScLnOKn4ZH/y66B4J5nyDQH8zd/2N9uPtuvGdPqJBSSQsoZw==","signatures":[{"sig":"MEQCIDenaQTHOY7bI2VkZN2Z3nw7S5TW7l6GtlFBoHg+Reb7AiAQptJykXtFXP6FqWqwC6X3FkSYJ5lOVz9SYutv7F0CUA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.1.0-beta.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":320550},"type":"module","module":"src/index.ts","exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"4d8783dd8e2e4336e82ae2b04a50e958a5d11bca","scripts":{"test":"bun test","check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"readmeFilename":"README.ru.md","devDependencies":{"effect":"4.0.0-beta.98","@types/bun":"latest","typescript":"^7.0.2"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.1.0-beta.2_1784199139365_0.39276296976631864","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@avytheone/efmesh","version":"0.2.0","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.2.0","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"aad8caacfc4ed796df8ab632f368917afab7e7f8","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.2.0.tgz","fileCount":43,"integrity":"sha512-xOzM9I4RyP3LOrhEBq66X279GWE97vIZjq1Pe14LG2pYbWY3BHY0jc+0zhLOPm8rQwd8SXC8GjvTLV2nFvh1fw==","signatures":[{"sig":"MEUCIQC5xn/vcml+Qpll8UGufcKJCNwhopV3taT8wiHPs2KubgIgOZwNttbQFGdsmrMu3mwiIIakHwOI2UT/fm5Mx/q6mtc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":386186},"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"88659e20a1048f6070858ff45f131f78fa0b2b9a","scripts":{"test":"bun test","check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","@types/bun":"latest","typescript":"^7.0.2"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.2.0_1784211430208_0.9665147453463012","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@avytheone/efmesh","version":"0.2.1","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.2.1","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"c7c55976da5e8d032562244c93407b5271d50239","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.2.1.tgz","fileCount":44,"integrity":"sha512-4UvYjiB/i6ZV9tzFoOa9nbEvJD2eQlDtaq5E3L4gIkug1uESa+Xn7CtI0C+2jD39ywX6uZ8DHwWjvIbFP0RShw==","signatures":[{"sig":"MEYCIQDfbCfmv6kO8aYWPOig4avj0hNWQaOAghgIDEaBotcMCAIhAIfO0wkGSszYZs0SzJXL4batYiZQ1encnQgmlJ/0wm/3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":378348},"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"43b0ca22614561b453ee8832512b8816b7c7ff52","scripts":{"test":"bun test","check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","@types/bun":"latest","typescript":"^7.0.2"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.2.1_1784219600315_0.4902149850792188","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@avytheone/efmesh","version":"0.2.2","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.2.2","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"33208e4517b5ff78cef0a4a7f406c3a4fbed4aa3","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.2.2.tgz","fileCount":68,"integrity":"sha512-lkQqFfmuhHcI0k5hBpN79rMMTDwausUhrhCaJdBHyW7c21Qwh3JOUAwgHKx0YDE8To8fLNl+ZA4KKt0GllnDag==","signatures":[{"sig":"MEUCIQC7MJBKUCqkwrGyu1gBRWPW5hL6107P+qd7eoPRS6cd1QIgFF0xbREmNc31TuaKhAd9ciXtepxEbvGoPR1MESTO8Bo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":422791},"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"0dbfa2e65f2e964a413cc95806958cd530ca781d","scripts":{"test":"bun test","check":"biome check . && tsc --noEmit","prepare":"lefthook install"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","lefthook":"2.1.10","@types/bun":"latest","typescript":"^7.0.2","@biomejs/biome":"2.5.4"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.2.2_1784306610413_0.5643287875263887","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@avytheone/efmesh","version":"0.3.0","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.3.0","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"3f2dbd7f6e9fe5f8e4a45ffe981302baccebbfbd","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.3.0.tgz","fileCount":70,"integrity":"sha512-cFxl9xxks8e0G8BjbvHhn6KMDNMrNhgr3UQq/XjnUQkWlK5WGZY58R1+HbLZkrbrwnpPKw9WmVL/zNi1dS/ajw==","signatures":[{"sig":"MEUCIQCngMTs2wZnbzqkHY7ovGJ3HQJ9PoJycckmHmC2kCugpwIgXnyjm34WImPfR73WvbsaHRpywoiAo+Z8P8DOnABYBjc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":483695},"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"f06fcbcc35a5d2b7751bdb0ee792db337e111aae","scripts":{"test":"bun test","check":"biome check . && tsc --noEmit","prepare":"lefthook install"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","lefthook":"2.1.10","@types/bun":"latest","typescript":"^7.0.2","@biomejs/biome":"2.5.4"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.3.0_1784318084771_0.3106581972421312","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@avytheone/efmesh","version":"0.3.1","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.3.1","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"f73f0b8d4521e7122eddc1fd90a8096af18a53b1","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.3.1.tgz","fileCount":70,"integrity":"sha512-gRKIOEt9sGVS86x+T+c+BcyE9Wj4KWgvz83lNBYXVy7EJBY5PEGpDhgj2tUJPSp4iZKwLljcWjplaAaEQBKkew==","signatures":[{"sig":"MEUCIQC1HT7M7LMmYyoQCsYdseBMtMDZRxHDKQr0F8IqDydzngIgGzMWuMEEIQPCbQSczze0VKEp1FNQwiEb+qJkxmDRFPs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":487254},"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"0a18f10234cd8102834f061ea559169558eaec34","scripts":{"test":"bun test","check":"biome check . && tsc --noEmit","prepare":"lefthook install"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","lefthook":"2.1.10","@types/bun":"latest","typescript":"^7.0.2","@biomejs/biome":"2.5.4"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.3.1_1784363381456_0.9895441830177427","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@avytheone/efmesh","version":"0.3.2","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.3.2","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"3be6c4dce8fd784db9e6cbcbd457b9c49f7a6fc3","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.3.2.tgz","fileCount":69,"integrity":"sha512-E7khHu9iVGMhKtKTAMmcglSJNBem5t8agpU6by+DgtJxQZxE0cKWmupcE+rjYjRQ/2XFhozBfKlSC+kewPgRMw==","signatures":[{"sig":"MEUCIQDW0zm+drm4TqFiKjxyMs4caxnnov9MDJZd2vYl0lRtFgIgWecZk8D5vj4M44c0Z5gzJ4z/+7k44oEgu0OEjJtvB9Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.3.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":462993},"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"3b773eebdf7ed52a75b5b268d390d21e6b14c0f0","scripts":{"test":"bun test","check":"biome check . && tsc --noEmit","prepare":"lefthook install"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","lefthook":"2.1.10","@types/bun":"latest","typescript":"^7.0.2","@biomejs/biome":"2.5.4"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.3.2_1784366990257_0.6393626781793176","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@avytheone/efmesh","version":"0.4.0","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","_id":"@avytheone/efmesh@0.4.0","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"bin":{"efmesh":"src/bin.ts"},"dist":{"shasum":"95b8d482abe6fe03224b120627c9db9364c4dc07","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.4.0.tgz","fileCount":76,"integrity":"sha512-9H+3HPIVnrxJ1HZ4p1TzUFiLJy96laLp8eh0DvCDkAr1GCgYHmX4Sc3xdDLvdsOHlJXBgNE1iYkmESeot+f45w==","signatures":[{"sig":"MEUCIBIA0otjzPXMvx95SuIvEcDyEwZ8gtsJq+4HMOnGnYbzAiEAtfm3dRhvQ0395ZbbLlGM/nM3uOR6yhCh76VTSi11abE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":554012},"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"exports":{".":"./src/index.ts","./browser":"./src/browser/index.ts","./testing":"./src/testing/index.ts"},"gitHead":"b5754a5bba7bb9384cb67e5f8054f89db00689ff","scripts":{"test":"bun test","check":"biome check . && tsc --noEmit","prepare":"lefthook install"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"repository":{"url":"git+https://github.com/avytheone/efmesh.git","type":"git"},"_npmVersion":"11.16.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","directories":{},"_nodeVersion":"24.18.0","dependencies":{"libpg-query":"^17.7.3","@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98"},"_hasShrinkwrap":false,"devDependencies":{"effect":"4.0.0-beta.98","lefthook":"2.1.10","@types/bun":"latest","typescript":"^7.0.2","@biomejs/biome":"2.5.4"},"peerDependencies":{"effect":"4.0.0-beta.98"},"_npmOperationalInternal":{"tmp":"tmp/efmesh_0.4.0_1784377417926_0.0754507449377182","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@avytheone/efmesh","version":"0.5.0","description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","license":"MIT","author":{"name":"Alexey Yakimanskiy"},"repository":{"type":"git","url":"git+https://github.com/avytheone/efmesh.git"},"homepage":"https://github.com/avytheone/efmesh#readme","bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"type":"module","module":"src/index.ts","engines":{"bun":">=1.3.11"},"bin":{"efmesh":"src/bin.ts"},"exports":{".":"./src/index.ts","./testing":"./src/testing/index.ts","./browser":"./src/browser/index.ts"},"scripts":{"check":"biome check . && tsc --noEmit","test":"bun test","prepare":"lefthook install"},"dependencies":{"@duckdb/node-api":"1.5.4-r.1","@effect/platform-bun":"4.0.0-beta.98","libpg-query":"^17.7.3"},"peerDependencies":{"effect":"4.0.0-beta.98"},"devDependencies":{"@biomejs/biome":"2.5.4","@types/bun":"latest","effect":"4.0.0-beta.98","lefthook":"2.1.10","typescript":"^7.0.2"},"gitHead":"b86518794d9df8cf36316f2a9c77f5cebf77f3e6","_id":"@avytheone/efmesh@0.5.0","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-OyVYlkYPn8uSFwofffqKp9fQxF1GKGydyrFp1Wk8mwxrmKXjZmDm2Yj9qFcUxqR+CiaMyhYf9Qjn8BeXmIz6wQ==","shasum":"3abb6ad68052e92d94b1edb31a2f46240ba08209","tarball":"https://registry.npmjs.org/@avytheone/efmesh/-/efmesh-0.5.0.tgz","fileCount":79,"unpackedSize":607694,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avytheone%2fefmesh@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA+sQPm8En/MUHguw+1WvQU8U0glG/LlKkL4Y1hvgxSkAiAUfDJ3gvYe1GpoyTpx9PmB9QPFsmxYK9KaIB2PMBjmMg=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bb81ba2c-c469-4be8-9fae-79cd26c34dcd"}},"directories":{},"maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/efmesh_0.5.0_1784381040841_0.5905999345494588"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-16T10:14:39.055Z","modified":"2026-07-18T13:24:01.358Z","0.1.0-beta.1":"2026-07-16T10:14:39.338Z","0.1.0-beta.2":"2026-07-16T10:52:19.513Z","0.2.0":"2026-07-16T14:17:10.333Z","0.2.1":"2026-07-16T16:33:20.456Z","0.2.2":"2026-07-17T16:43:30.567Z","0.3.0":"2026-07-17T19:54:44.930Z","0.3.1":"2026-07-18T08:29:41.619Z","0.3.2":"2026-07-18T09:29:50.424Z","0.4.0":"2026-07-18T12:23:38.078Z","0.5.0":"2026-07-18T13:24:01.054Z"},"bugs":{"url":"https://github.com/avytheone/efmesh/issues"},"author":{"name":"Alexey Yakimanskiy"},"license":"MIT","homepage":"https://github.com/avytheone/efmesh#readme","keywords":["data-engineering","sqlmesh","dbt","duckdb","postgres","effect","bun","data-transformation","incremental","lakehouse"],"repository":{"type":"git","url":"git+https://github.com/avytheone/efmesh.git"},"description":"sqlmesh-like data transformation framework on TypeScript, Bun and Effect","maintainers":[{"name":"avytheone","email":"yakimanskiyav@yandex.ru"}],"readme":"# efmesh\n\n> Data transformation in the spirit of [sqlmesh](https://sqlmesh.com) — on TypeScript, [Bun](https://bun.sh) and [Effect](https://effect.website).\n\n[![ci](https://github.com/avytheone/efmesh/actions/workflows/ci.yml/badge.svg)](https://github.com/avytheone/efmesh/actions/workflows/ci.yml) ![status](https://img.shields.io/badge/status-beta-orange) ![npm](https://img.shields.io/npm/v/%40avytheone%2Fefmesh) ![license](https://img.shields.io/badge/license-MIT-green) ![runtime](https://img.shields.io/badge/runtime-bun-black) ![effect](https://img.shields.io/badge/effect-v4-5C4EE5)\n\nModels are plain TypeScript modules: SQL bodies, imports as dependencies, Effect Schema as the data shape. efmesh fingerprints every model by its canonical AST, keeps versions as snapshots, computes a plan as the diff between your project and an environment, and applies exactly that plan: physical tables are rebuilt only where something actually changed, while environments (dev/prod/…) are virtual views over shared physical storage — promoting to prod costs zero recomputation.\n\n<p align=\"center\"><img src=\"https://raw.githubusercontent.com/avytheone/efmesh/main/docs/demo.svg\" alt=\"efmesh demo: a ref typo is a compile error; a plan rebuilds exactly the changed branch; promotion is a view swap\" width=\"840\"></p>\n\n```ts\nimport { Schema } from \"effect\"\nimport { defineModel, kind } from \"@avytheone/efmesh\"\nimport { rawMoves } from \"./sources.ts\"\n\nexport const moves = defineModel(\n  {\n    name: \"med.moves\",\n    kind: kind.incrementalByTimeRange({\n      timeColumn: \"moved_at\",\n      start: \"2026-01-01T00:00:00Z\",\n      lookback: 1, // the tail is re-read — late-arriving data catches up\n    }),\n    schema: Schema.Struct({ case_id: Schema.String, moved_at: Schema.DateTimeUtc }),\n  },\n  (ctx) => ctx.sql`\n    SELECT ${ctx.cols(rawMoves, \"case_id\", \"moved_at\")}\n    FROM ${ctx.ref(rawMoves)}\n    WHERE moved_at >= ${ctx.start} AND moved_at < ${ctx.end}\n  `,\n)\n```\n\n## Why not dbt / sqlmesh\n\n|                     | dbt                   | sqlmesh                | efmesh |\n|---------------------|-----------------------|------------------------|--------|\n| Model language      | SQL + Jinja           | SQL + Jinja/Python     | SQL inside TypeScript |\n| Dependencies        | `ref('string')`       | SQL parsing            | module imports — checked by the compiler |\n| Column typing       | no                    | contracts (runtime)    | Effect Schema: compile-time + a contract before every build |\n| Versioning          | none (state-less)     | snapshots + fingerprint | snapshots + fingerprint |\n| Dev environments    | table copies          | virtual (views)        | virtual (views) |\n| Incrementality      | hand-rolled `is_incremental()` | intervals, tracked | intervals, tracked, resumable |\n| Parquet lake        | adapters              | adapters               | native: `target: \"parquet\"`, interval = partition |\n| Multi-dialect       | yes                   | yes (sqlglot)          | **no** — your engine's dialect (DuckDB or Postgres) |\n\nA typo in a `ref` is a compile error, not an empty run; renaming a parent's column breaks the child's build before any SQL reaches the database.\n\n## Why this exists: small data lakes\n\nMost analytics in the world is not a cloud warehouse — it is DuckDB-class data: gigabytes to a terabyte on one machine. Product analytics of a startup, the marts of one department, on-prem and edge deployments, a pipeline living inside a SaaS app. dbt and sqlmesh were born in the cloud-DWH world and carry that weight with them (Python, adapters, infrastructure). efmesh is the sqlmesh approach that is `bun add` and go — and the lake is a folder of parquet files.\n\nHonestly placed: the **core** is the same class of system as sqlmesh — snapshots, AST fingerprints, plans as diffs, virtual environments. The **breadth** is not: no cloud engines, no multi-dialect, no web UI, no ecosystem of packages — and no ambition to catch up on all of it. Compared to dbt-core the trade is reversed: dbt has an industry around it, but no state-based plans, no virtual environments, no fingerprint versioning — every team reinvents \"how not to rebuild everything\". Our four honest advantages: **types as the DAG contract** (a typo breaks the build, not the nightly run), **library before CLI** (embed `Efmesh.apply(...)` in your app), **one language for the whole stack** (models, app, tests), and a **codebase you can read in an evening** (~10k lines).\n\n## Who this is for (and who it isn't)\n\n**For you**, if you are a TypeScript team on Bun, want a typed dbt/sqlmesh-style workflow on top of DuckDB or Postgres, and are fine living on a beta (efmesh is 0.5.x; Effect v4 is beta, pinned exactly as a peer dependency).\n\n**Not for you**, if you need: a Node runtime (Bun-only for now), multi-dialect or cloud DWHs (Snowflake/BigQuery are out of scope), 1.0-grade stability, or the Python ecosystem — take sqlmesh instead, honestly.\n\n## Features\n\n**Models.** `full`, `view`, `embedded` (inlined subquery, no materialization), `incrementalByTimeRange` (interval ledger, batched backfill, lookback), `incrementalByUniqueKey` (upsert), `scdType2` (row history, `valid_from`/`valid_to` managed by efmesh), `defineExternal` (tables, parquet/csv/json files, URLs), `defineSeed` (CSV/JSON reference data, content hash in the fingerprint), `defineSqlModel` (raw `.sql` files with `@ref`/`@start`/`@end`).\n\n**Materialization targets.** Native engine tables, `parquet` (a lake, local or s3://, interval = partition, views over `read_parquet`), `ducklake` (table-per-fingerprint in a [DuckLake](https://ducklake.select) catalog — catalog snapshots and time travel come as a bonus).\n\n**Plans and versions.** Fingerprints over canonical ASTs (reformatting SQL never triggers a rebuild — frozen by golden tests), change categorization breaking / non-breaking / indirect / forward-only with `plan --explain` reasoning and a `--reclassify` operator override, indirect physics reuse (descendants of a non-breaking change are not rebuilt — scdType2 keeps its history), `--forward-only` applies a change without replaying history (the new version inherits physical storage and done-intervals; new columns via `ALTER`), plan confirmation in a TTY, an applied-plans journal with `applied_by`.\n\n**Data quality.** A schema contract before every build (`DESCRIBE` of the query against the declared Schema), `notNull` / `unique` / `accepted` audits (blocking fails the apply, `warn` logs), continuity gates (`assertContiguous`, `assertNoGaps`) that compute coverage and refuse with the boundaries of the hole, a declared scope so an audit means the same thing to `apply` and to the standalone `efmesh audit` over an environment's view layer, and `testModel` — unit tests for models on fixtures in in-memory DuckDB.\n\n**Operations.** `run` — an idempotent scheduler tick for cron/systemd; `apply` and `run` of an environment share one cross-process lock (stale locks of crashed processes are reclaimed by ttl); DAG concurrency `--jobs` (a model starts as soon as its parents are ready); batch retries `--retries`; a janitor for orphaned physical storage (removal is a transactional claim — the race against a concurrent apply is closed); `efmesh compact` for the small files a micro-batch writer leaves in a settled partition; `--metrics <path>` writes an OpenMetrics file node_exporter scrapes without a wrapper; a versioned state-store schema + `efmesh migrate` (with a store file backup).\n\n**Serving and trust.** A `manifest.json` beside every parquet version names its file set, schema and answer passport, so a client needs one fetch instead of a directory listing — `@avytheone/efmesh/browser` turns it into a duckdb-wasm relation. `efmesh passport <env>` reports what an environment's data may be believed to answer, narrowed by the DAG to the worst value over a model's ancestry. A redacted environment materializes its own physics in which the declared sensitive columns were never written, because a masking view protects nothing once clients read the files.\n\n**Engines.** DuckDB (default, including httpfs/ATTACH federation) and Postgres (`Bun.SQL` pool, canonicalization via libpg_query, parallel backfill). State store: SQLite next to the project, or a schema in Postgres.\n\n## Quickstart\n\n```sh\nbun add -d @avytheone/efmesh\nbunx efmesh init my-warehouse && cd my-warehouse\nbunx efmesh plan dev    # what would be done\nbunx efmesh apply dev   # physical tables, backfill, view layer\n```\n\n`init` scaffolds a working skeleton: `efmesh.config.ts`, example models, a seed. From there, edit models and iterate with `plan`/`apply`; the full lifecycle:\n\n```sh\nbunx efmesh apply dev            # apply changes to dev\nbunx efmesh audit dev            # audit what the environment serves right now\nbunx efmesh apply prod --yes     # promotion: view swap, no recomputation\nbunx efmesh run prod             # cron tick: catch up on new intervals\n```\n\nLive examples: [examples/hospital](https://github.com/avytheone/efmesh/tree/main/examples/hospital) — patient movements across hospital departments, every model kind and target; [examples/eventlake](https://github.com/avytheone/efmesh/tree/main/examples/eventlake) — the [canonical table over an event lake](#event-lake-canonical-table).\n\n## How it works\n\n```\nmodels (TS modules)  ──►  DAG + fingerprints over canonical ASTs\n                               │\n                     plan = diff against the state store\n                               │\n      apply: physical tables ── interval backfill ── audits ── view layer\n                               │\n          state store: snapshots, intervals, environments, journal\n```\n\n- **Physical layer** — tables `_efmesh.<model>__<fp8>` (or parquet prefixes / DuckLake): a version is a table; the old one lives until the janitor collects it.\n- **Virtual layer** — views `<env>__<schema>.<table>` (prod is just `<schema>.<table>`) pointing at physical storage. An environment is a set of pointers; promotion and rollback are view swaps.\n- **The interval ledger** is the single source of truth about what has been computed: an interrupted backfill resumes where it stopped; recomputing an interval is a transactional DELETE+INSERT of the range — no duplicates.\n\nFull architecture, invariants and decisions: [SPEC.md](https://github.com/avytheone/efmesh/blob/main/SPEC.md).\n\n## Data quality\n\n```ts\n// an audit is a SQL predicate over violations; blocking fails the apply, warn logs\naudits: [\n  audit.notNull(\"case_id\"),\n  audit.unique(\"case_id\", \"moved_at\"),\n  audit.warn(audit.accepted(\"dept\", [\"ICU\", \"surgery\", \"therapy\"])),\n]\n```\n\n```ts\n// a model unit test: fixtures → CTEs → in-memory DuckDB → comparison (bun test)\nimport { testModel } from \"@avytheone/efmesh/testing\"\n\ntest(\"stays\", () =>\n  testModel(stays, {\n    inputs: { [moves.name.full]: [{ case_id: \"c1\", moved_at: \"2026-01-01T10:00:00Z\" }] },\n    expect: [{ case_id: \"c1\", duration: null }],\n  }))\n```\n\nThe declared `schema` is a contract, not documentation: before every build efmesh runs `DESCRIBE` on the query and fails with `SchemaMismatchError` if column names or types diverge. NULL guarantees are expressed with the `notNull` audit.\n\nAn audit is evaluated at two different scopes: `apply` checks the interval it just wrote, `efmesh audit` checks the whole environment view. For a row-wise predicate those agree. For an aggregate one they can disagree on correct data — uniqueness that holds inside every written interval and legitimately fails across the table is exactly what a de-duplication window produces. Say which you meant:\n\n```ts\naudits: [\n  // a windowed guarantee: true of each interval, not of the table\n  audit.perInterval(audit.unique(\"event_id\")),\n  // a cross-interval invariant: checked before promotion, never against a slice\n  audit.whole(audit.unique(\"case_id\", \"valid_from\")),\n]\n```\n\nTwo audits compute coverage rather than trusting a flag:\n\n```ts\naudits: [\n  audit.assertContiguous(\"batch_no\"),          // no holes in the sequence\n  audit.assertNoGaps(\"happened_at\", \"day\"),    // no missing daily buckets\n]\n```\n\nThey refuse with the numbers, not a count — `covered through 2026-01-02, resumes at 2026-01-04 (and 2 further gap(s))` — so an operator knows what to restate without writing a query first. Refuse with numbers before the first write, rather than succeed with silently lost history. Both look for holes inside the observed range and assume neither end of it: a late start or a missing tail is a freshness question, and `efmesh passport` already answers that from the interval ledger.\n\n`efmesh audit` reports a `perInterval` audit as skipped rather than answering a question it was never asked, so a clean run is not mistaken for full coverage. An unscoped audit runs everywhere, which is what audits did before scopes existed. One edge: the interval pass audits the batch as rendered, and with the default `batchSize` a fresh backfill is a single wide window — a model whose invariant depends on that width must pin `batchSize: 1`. `plan` warns about exactly that shape (`window-over-batch`) when it sees a window function over a batch wider than one interval.\n\n## Event-lake canonical table\n\nAn at-least-once archiver writing into a partitioned lake makes duplicates\n**legal** there. `count(*)` over the raw files then counts redeliveries as data\n— in the incident this recipe comes from, by 3.8× (179 095 rows against 46 875\ndistinct event ids). Every reader needs a canonical layer above the lake: one\nrow per event id, typed columns, derived values computed once.\n\n```ts\nexport const rawEvents = defineExternal({\n  name: \"raw.events\",\n  // partitions may hold additively different schemas — a positional scan would shear them\n  source: external.files(\"archive/**/*.parquet\", \"parquet\", {\n    unionByName: true,\n    hivePartitioning: true,\n  }),\n  schema: Schema.Struct({ event_id: Schema.String, arrived_at: Schema.DateTimeUtc /* … */ }),\n})\n\nexport const events = defineModel(\n  {\n    name: \"core.events\",\n    // increment by ARRIVAL time: the only clock the archiver controls\n    kind: kind.incrementalByTimeRange({ timeColumn: \"arrived_at\", start: \"…\", batchSize: 1 }),\n    schema: Schema.Struct({ event_id: Schema.String /* … */ }),\n    grain: [\"event_id\"],\n  },\n  (ctx) => ctx.sql`\n    SELECT event_id, occurred_at, arrived_at, metric_value FROM (\n      SELECT\n        ${ctx.cols(rawEvents, \"event_id\", \"occurred_at\", \"arrived_at\")},\n        CAST(metric_value AS DOUBLE) AS metric_value,\n        -- the dedup key and its tie-breakers, declared: first arrival wins\n        row_number() OVER (\n          PARTITION BY event_id ORDER BY arrived_at ASC, archiver_offset ASC\n        ) AS copy_rank\n      FROM ${ctx.ref(rawEvents)}\n      -- read three days BEYOND the interval, write only the interval\n      WHERE arrived_at >= ${ctx.start} - INTERVAL 3 DAY AND arrived_at < ${ctx.end}\n    ) horizon\n    WHERE copy_rank = 1 AND arrived_at >= ${ctx.start}\n  `,\n)\n```\n\n**What this guarantees, exactly.** An incremental tick renders one `[start,\nend)` and DELETE+INSERTs that range, so a window function only ever sees what\nits own query reads. Reading a horizon back beyond `start` and keeping the\n*first* copy makes the suppression work across intervals without rewriting a\nsettled one — the original stays put, the late copy is dropped in the interval\nit arrived in. Hence:\n\n> Duplicates are eliminated when the original arrived within the horizon. A\n> duplicate that arrives after the horizon has passed is **not** eliminated —\n> it enters the canonical table as a second row. Cross-horizon redeliveries are\n> the source's responsibility.\n\nThat residual is surfaced, not hidden: a view over the canonical table listing\nids with more than one row, carrying a warn-level audit, puts any occurrence in\nthe log of every apply. A globally unique guarantee would need a time-range scan\ncombined with an upsert by key — a materialization efmesh does not have today.\n\nFull recipe, with a committed dirty fixture and the numbers asserted in\n`test/eventlake.test.ts`:\n[examples/eventlake](https://github.com/avytheone/efmesh/tree/main/examples/eventlake).\n\n## Configuration\n\n`efmesh.config.ts` is a typed TS module — no YAML:\n\n```ts\nimport { defineConfig } from \"@avytheone/efmesh\"\n\nexport default defineConfig({\n  discovery: \"models/**/*.ts\",      // every model export by glob; duplicate names = load error\n  // models: [a, b, c],             // …or by value (can be combined with discovery)\n\n  // engine: a DuckDB file by default; Postgres is one line\n  engine: { path: \"efmesh.duckdb\" },          // or { url: \"postgres://…\", max: 8 }\n  state: { path: \"efmesh.state.sqlite\" },     // or { url: \"postgres://…\" }\n\n  lake: { path: \"lake\" },                     // for target: \"parquet\"; local or s3://\n  ducklake: { catalog: \"ducklake.sqlite\", dataPath: \"lake/ducklake\" },\n  attach: { reporting: { url: \"reporting.duckdb\" } },  // export targets by alias\n})\n```\n\n## CLI\n\n| Command | What it does |\n|---|---|\n| `efmesh init [dir]` | scaffold a project: config, example models, a seed |\n| `efmesh plan <env>` | diff the project against an environment + missing intervals; changes nothing |\n| `efmesh apply <env>` | plan → confirmation (TTY) → physical tables, backfill, view layer; `--json` |\n| `efmesh run <env>` | scheduler tick: new intervals only, under the lock; for cron; `--json` |\n| `efmesh restate <env> --model m --from t --to t` | replay a past range for a model and its descendants; `--dry-run`, `--json` |\n| `efmesh status <env>` | what is going on: last plan, interval lag, recent run ticks; `--json`, `--check` |\n| `efmesh audit <env>` | audit the environment's view layer — catches after-the-fact degradation |\n| `efmesh passport <env>` | what the environment's data can be trusted to answer; `--json` |\n| `efmesh diff <envA> <envB>` | how two environments differ; `--data` compares the actual data |\n| `efmesh render <model> [--env] [--json]` | the final SQL of a model |\n| `efmesh lineage <model[.col]> [--json]` | column lineage down to the raw sources |\n| `efmesh graph [--html] [--json]` | the model DAG as text, an HTML page, or JSON |\n| `efmesh janitor [--ttl 7] [--json]` | remove orphaned physical storage older than ttl |\n| `efmesh compact [--dry-run] [--json]` | merge a settled partition's small files into one |\n| `efmesh migrate [--json]` | bring the state-store schema up to the current version |\n| `efmesh schedule <env>` | register `run <env>` in the OS scheduler via `Bun.cron` (`--list [--json]`) |\n\n`apply`/`run` share `--jobs N` — DAG concurrency (always 1 on DuckDB — single connection) — and `--retries N` — retries for transient batch failures (exponential backoff). `apply` also takes `--yes`/`-y` — skip confirmation (required in a non-TTY when the plan has changes) — and `--forward-only <model>,…` — reuse physical storage and history.\n\n`plan`/`apply` take `--reclassify <model>=breaking|non-breaking[,…]` — the\noperator's verdict on top of `--explain`, journaled with `applied_by`. A\nnon-breaking parent lets unchanged descendants reuse their previous physical\ntables instead of rebuilding (scdType2 keeps its row history); an override\nthat plainly contradicts the AST (dropped columns) is refused.\n\n`restate <env> --model <m> --from <t> --to <t>` replays a past time range when\nbad source data arrived after the fact: it clears the range's done-intervals\nfor the `incrementalByTimeRange` model **and its incrementalByTimeRange\ndescendants** (the cascade is the planner's ordinary missing-interval logic),\nso the next `apply` — or a `run` tick — recomputes exactly that range. It\nmutates only the interval ledger, under the environment lock, and never touches\nthe physics directly (the ensuing backfill's DELETE+INSERT does). Bounds are\nISO UTC and must be aligned to the model's grain (a misaligned bound is a typed\nerror); `scdType2` is refused by name (no time-range semantics over version\nhistory). `--dry-run` prints what would be recomputed and changes nothing;\n`--json` for CI.\n\nEvery command with something to report speaks `--json` — `plan`, `apply`,\n`run`, `audit`, `status`, `passport`, `diff`, `graph`, `janitor`, `compact`,\n`migrate`, `lineage`, `render` and `schedule --list` — a stable machine-readable shape (a contract\nunder semver) for CI and bots; exit codes are unchanged, stdout stays pure\nJSON (logs go to stderr). `apply --json` returns `{env, applied, plan, built,\npromoted}` and `run --json` returns `{env, outcome, processed, blockedBy?}` —\nboth emit their payload even on exit 2 (a non-TTY `apply` that needs `--yes`,\nor a `run` blocked by structural changes), so a bot always reads *why* nothing\nran. `status --json` returns `lastPlan.summary` and each `ticks[].detail` as\nstructured objects, not JSON encoded inside a string. Each shape is a JSON\nobject carrying a top-level `apiVersion` (currently `1`) — a single integer a\nreader pins on, bumped only when a field breaks; new fields stay additive.\n\n`plan --explain` adds the reasoning to every change: which canonical-AST\nnodes diverged (`where_clause`, `select_list[2] (added)`, …) and why the\ncategory followed — including cascade sources for `indirect`. The same\ndata ships in `--json` as `explain`; the AST paths are a debugging hint,\nnot part of the contract.\n\n`diff <envA> <envB> --data` compares the actual data of two environments:\nrow counts, key overlap (grain or the kind's key), per-column mismatch\nrates among matched keys, schema drift between sides. `--sample P` (1–99)\ncompares a deterministic share of keys — md5 buckets aligned across both\nsides, so sampling never fabricates only-in rows. `--model a,b` narrows,\n`--json` for CI.\n\n`schedule <env> [--cron '@hourly']` registers the `run` tick in the OS\nscheduler (crontab / launchd / Task Scheduler) via `Bun.cron` — idempotent\nby title, `--remove` unregisters, `--list` shows what's there. Honest\ncaveats: OS cron runs in the local timezone and does not catch up on missed\nruns, and Arch-family Linux ships no cron daemon at all — `--print-systemd`\nemits user-unit files instead (`Persistent=true` catches up). Overlapping\nticks are safe by construction: `run` takes the env lock and exits `2` when\nchanges await a human.\n\n### Compaction\n\nA micro-batch writer leaves hundreds of tiny files in a partition, and a\npartition of hundreds of tiny files is what destroys the query planner — the\nsmall-files problem, the second universal event-lake pain after duplicates.\n`efmesh compact` merges each settled partition into one file, de-duplicating by\nthe declared key on the way through.\n\n**What it will touch.** Targets come from the project and from nowhere else:\nefmesh's own parquet partitions (a `target: \"parquet\"` model incremented by time\nrange — its `grain` is the dedup key), plus the `defineExternal` sources that\nopted in explicitly. There is no way to point `compact` at a directory; a lake\nefmesh does not own is compacted only where its declaration says so:\n\n```ts\nexport const rawEvents = defineExternal({\n  name: \"raw.events\",\n  source: external.files(`${archive}/**/*.parquet`, \"parquet\", { unionByName: true }),\n  schema: Schema.Struct({ event_id: Schema.String, arrived_at: Schema.DateTimeUtc }),\n  maintenance: {\n    compact: {\n      partitionKey: \"arrival_date\", // the hive key whose value dates a partition\n      uniqueKey: [\"event_id\"],      // one row per key survives the merge\n      orderBy: [\"arrived_at\"],      // …and it is the first arrival, not an arbitrary copy\n      graceMinutes: 10,             // wait past the newest file's mtime\n    },\n  },\n})\n```\n\nThe policy is declaration-only: it never enters the fingerprint, so adopting\ncompaction does not rebuild anything.\n\n**Concurrency: cooperative, not transactional.** This is the difference to read\nbefore trusting it. `janitor` takes a **transactional claim** through the state\nstore — two janitors cannot remove the same snapshot, because the claim and the\ndelete are one atomic step. `compact` has **no such claim**. It coordinates with\nthe lake's writer through file conventions and timing alone:\n\n- it never touches a partition dated today or later (the live writer owns it);\n- it waits out a grace period measured from the newest file's mtime, because a\n  batch may still be landing;\n- it publishes through a `.tmp` and an atomic rename, so a reader sees either\n  the old files or the merged one, never a partial write;\n- it deletes only the files it listed *before* the merge, so a file that arrives\n  mid-run is left in place rather than lost.\n\nThose rules make compaction safe against a well-behaved **appending** writer.\nThey do not make it safe against a writer that rewrites or deletes files in\nplace, and they do not serialize two concurrent compactors. Do not ascribe\njanitor's guarantees here — the mechanism does not deliver them.\n\nThe merge is `SELECT * EXCLUDE (_rn)` over `read_parquet(…, union_by_name =\ntrue)` — never an explicit column list, so a column the writer started emitting\nafter the policy was declared survives, and a transition-day partition holding\ntwo schema generations merges instead of failing. `--dry-run` reports what would\nbe merged and writes nothing; `--model` narrows to one model; `--grace`\noverrides the declared wait. `--json` reports every partition left alone with\nthe reason (`current-day`, `grace-period`, `already-compact`, `undated`).\n\n### Exit codes\n\nThe single contract for headless callers (CI, cron, agents); changing it is a\nSemVer event. Referenced from the CLI's own `--help` and by every command:\n\n| Code | Meaning | When |\n|---|---|---|\n| `0` | success | the command did its job |\n| `1` | error | any failure — bad config, an engine/state error, a blocking audit violation |\n| `2` | awaiting a human | not a failure: `apply` has changes but no `--yes` in a non-TTY, or `run` met unapplied structural changes |\n\nNothing ever blocks waiting for input without announcing it: the only prompt is\n`apply`'s confirmation, and it appears solely at an interactive TTY — a non-TTY\n`apply` with changes refuses with code `2` instead of hanging. efmesh will not\nsilently roll out a plan nobody has seen.\n\n### Alerting\n\n`status <env> --check` turns the report into a health probe: it exits non-zero\nwhen the environment is **unhealthy** — a stuck backfill (failed intervals) or\na last tick that ended in `error`. Normal states never trip it: `awaiting-human`\n/ `lock-held` ticks, plain lag (a tick simply hasn't caught up yet), and a\nnever-applied environment all stay exit `0`. It still prints the report, so an\noperator paged by it sees the reason.\n\nIt composes with a scheduled `run` and systemd `OnFailure=`: point the timer's\nfailure handler at a check, and let a dead-man's-switch service (e.g.\n[healthchecks.io](https://healthchecks.io)) page when the check itself stops\nreporting.\n\n```ini\n# efmesh-run@.service — the hourly tick\n[Service]\nExecStart=/usr/bin/env efmesh run %i\nOnFailure=efmesh-alert@%i.service      # fires on a run that exits 1\n\n# efmesh-alert@.service — probe health, then ping the dead-man's switch\n[Service]\nType=oneshot\nExecStart=/usr/bin/env efmesh status %i --check\nExecStart=/usr/bin/curl -fsS https://hc-ping.com/<uuid>/${EXIT_STATUS}\n```\n\nExit `2` (a `run` blocked by structural changes) is *not* a failure and does\nnot fire `OnFailure`; it means a human must `apply` — see [exit codes](#exit-codes).\n\n## Logging\n\n`apply` and `run` narrate what they do. Logs go to **stderr** — stdout stays\nreserved for the plan screen, summaries and `--json`, which stays byte-clean.\nLevels, set by the built-in `--log-level` flag (minimum level, default `info`):\n\n- **info** — lifecycle a human watches: per-model build start/finish with\n  duration, backfill batch progress (`batch 3 of 7` with the interval bounds),\n  promotion.\n- **warn** — warn-audits (violations that do not block) and retries.\n- **debug** — the rendered SQL about to run, lock acquire/release, and other\n  internals. `--log-level debug` also prints the full fiber trace on a failure.\n\nEach line carries structured fields as annotations (`model`, `env`, `interval`,\n…). At a TTY the output is pretty and colored; piped to a file or the systemd\njournal it is one-line [logfmt](https://brandur.org/logfmt) with no ANSI, so a\nlog reader (or an AI agent post-morteming a 3am tick) can group by field.\n\nEmbedding efmesh as a library? Logging is Effect's `Effect.log*` — provide your\nown `Logger` layer (sink, format, minimum level) and the CLI's choices do not\napply. Row counts are not logged: efmesh never runs an extra query just to count.\n\n## Serving a lake to a browser\n\nA parquet materialization writes a `manifest.json` beside each version:\n\n```json\n{\n  \"manifestVersion\": 1,\n  \"model\": \"core.events\", \"fingerprint\": \"fdf6b3cc\",\n  \"files\": [\"./interval=2026-03-01/data.parquet\", \"…\"],\n  \"schema\": [{ \"name\": \"event_id\", \"type\": \"text\" }, { \"name\": \"arrived_at\", \"type\": \"temporal\" }],\n  \"intervals\": [{ \"start\": \"…\", \"end\": \"…\" }],\n  \"answerable\": \"sampled\",\n  \"caveats\": [\"observation starts on 2026-03-01\"],\n  \"freshness\": { \"contiguousThrough\": \"…\", \"latestInterval\": \"…\", \"failedIntervals\": 0 },\n  \"effective\": { \"answerable\": \"sampled\", \"caveats\": [{ \"model\": \"raw.events\", \"text\": \"…\" }],\n                 \"completeThrough\": \"…\", \"limitedBy\": \"raw.events\" },\n  \"redacted\": []\n}\n```\n\nBrowsers cannot glob over HTTP, so without it a client walks a web server's\ndirectory listings — fragile, slow, and able to catch a partition mid-rewrite.\nWith it, one fetch names the file set. `@avytheone/efmesh/browser` turns that\ninto a duckdb-wasm relation:\n\n```ts\nimport { fetchManifest, registerModel, passportOf } from \"@avytheone/efmesh/browser\"\n\nconst url = \"https://lake.example.com/core/events/fp=fdf6b3cc/manifest.json\"\nconst manifest = await fetchManifest(url)\nconst relation = await registerModel(db, url, manifest)     // read_parquet([...], union_by_name = true)\nawait connection.query(`SELECT count(*) FROM ${relation}`)\n\npassportOf(manifest)   // { answerable, caveats, completeThrough, limitedBy, hasGaps }\n```\n\nIt is a subpath, not a separate package, on purpose: the helper and the format\nare one contract, and two packages would let a client pin versions that disagree\nabout the document they exchange. The subpath imports nothing else from efmesh —\nno Effect, no DuckDB bindings, no node builtins.\n\n`freshness` is **derived from the interval ledger, never declared**:\n`contiguousThrough` stops at the first gap even when later intervals exist, so a\nclient cannot present a partial total as complete. `answerable` and `caveats` are\nyours to declare on the model — the limits of trust travel with the data instead\nof living in someone's dashboard note. `effective` is that passport narrowed by\nthe model's ancestry; see below.\n\n## The answer honesty passport\n\nA schema contract says what a column *is*. The passport says what an answer may\nbe **believed** — the thing a consumer actually needs before rendering a number.\n\n```ts\ndefineModel({\n  name: \"mart.stays\",\n  answerable: \"sampled\",\n  caveats: [\"observation starts on 2026-03-01 — earlier stays are partly visible\"],\n  // …\n})\n```\n\nFreshness is not yours to declare: it comes from the interval ledger, because a\nhand-maintained badge drifts from the data the moment one backfill fails.\n\nBoth halves then travel the DAG, which is the part a hand-written convention\nalways gets wrong. A mart whose source is complete only through Tuesday is\ncomplete only through Tuesday, whatever its own ledger says — it computed\nWednesday over data that was not there yet. So the effective passport is the\nworst value over the model and its ancestors, and it names the one that imposed\nthe limit:\n\n```console\n$ efmesh passport dev\nenvironment \"dev\": what its data can be trusted to answer\n  ✓ med.moves  full, complete through 2026-07-18T00:00:00.000Z\n  ~ mart.stays  sampled — declared full, complete through 2026-07-17T00:00:00.000Z (limited by med.moves)\n      · observation starts on 2026-03-01 [from raw.moves]\n```\n\n`--json` carries `declared` and `effective` side by side: a client renders the\neffective value, and a human debugging why it degraded needs the difference.\nRead it for every model an environment serves — not only the parquet ones, which\nare the only models a `manifest.json` can reach.\n\n## Redacted environments\n\nOnce clients read the files directly, a masking view protects nothing — a view\nis not a security boundary. So a redacted environment gets **its own physics**,\nin which the redacted columns were never written:\n\n```ts\n// the model declares what is sensitive\ndefineModel({ name: \"core.people\", redact: [\"ssn\"], /* … */ }, …)\n\n// the config declares which environments materialize redacted\nexport default defineConfig({\n  environments: { safe: { redacted: true } },\n})\n```\n\n```sh\nefmesh apply dev --yes     # dev__core.people  → id, name, ssn\nefmesh apply safe --yes    # safe__core.people → id, name\n```\n\nTwo physical tables, and `ssn` is absent from the second — not filtered out of a\nview over the first. Models that declare no policy are untouched and keep sharing\nphysics across environments, so this costs storage only where it buys something.\n\n> **What this is not.** A redacted environment is *safe defaults* — agents and\n> dev environments see clean data unless someone deliberately points them\n> elsewhere. It is **not** access control over the physical storage: anyone who\n> can read the unredacted environment's files can read the unredacted data. That\n> boundary belongs to your bucket policy and filesystem permissions. efmesh\n> guarantees the redacted physics does not contain the columns; nothing more.\n\n## Metrics\n\n`apply` and `run` take `--metrics <path>` and write a Prometheus/OpenMetrics\ntext file after the command — the dialect\n[node_exporter's textfile collector](https://github.com/prometheus/node_exporter#textfile-collector)\nparses, so a scraped host needs no wrapper around efmesh:\n\n```ini\n# efmesh-run@.service\nExecStart=/usr/bin/env efmesh run %i --metrics /var/lib/node_exporter/efmesh.prom\n```\n\n```\n# HELP efmesh_intervals_done_total how many intervals were computed and marked done\n# TYPE efmesh_intervals_done_total counter\nefmesh_intervals_done_total{model=\"med.moves\",env=\"dev\"} 1\n# TYPE efmesh_model_build_duration_seconds gauge\nefmesh_model_build_duration_seconds{model=\"med.moves\",env=\"dev\"} 0.015\n# TYPE efmesh_last_run_timestamp_seconds gauge\nefmesh_last_run_timestamp_seconds{outcome=\"ok\"} 1784367897\n```\n\nSeries: intervals done/failed, snapshots built, audits passed/failed, per-model\nbuild duration, command duration, planned models by change category, and the\ntimestamp of the last finished command by outcome. Per-model series carry\n`model` and `env` labels.\n\nThe timestamp is the one to alert on, because it is what makes a *silent*\nprocess loud — a tick that never fired writes nothing, so the metric goes stale\neven though no error was ever reported:\n\n```promql\ntime() - max(efmesh_last_run_timestamp_seconds) > 5400   # hourly tick, 90 min of grace\n```\n\nThe file is written through a temp file and renamed, so a scraper reading it\nmid-write is impossible. It is written on every finished command — including a\ntick that found no work and an `apply` that exited `2` awaiting confirmation —\nbecause \"ran and did nothing\" and \"did not run\" must not look alike. An\nunwritable path is a warning, never a failed apply.\n\nRow counts are absent on purpose: efmesh never runs an extra query to count\nrows, and inventing the number would mean measuring something else. Embedding as\na library? The metrics are Effect's `Metric` registry\n([SPEC §10.1](https://github.com/avytheone/efmesh/blob/main/SPEC.md)) — read it\nyourself with `Metric.snapshot` and ship it wherever you like.\n\n## Next to a codebase on a different Effect major\n\nefmesh pins Effect v4 exactly, as a peer dependency. Two Effect majors cannot\nshare one process — `Schema` identity and the context registry are per-instance\n— so if your platform runs Effect v3, do not import efmesh into it. The failure\nis at least loud and immediate: the import throws (`Export named 'Semaphore' not\nfound`), never a subtly wrong runtime.\n\nThe recipe is to keep the warehouse a separate package and talk to it as a\nprocess:\n\n1. **Its own `package.json`** holding `@avytheone/efmesh` and its `effect` peer,\n   with your models and config beside them. A nested directory is fine, and so\n   is a workspace in a monorepo: bun and npm install incompatible versions\n   per-package rather than hoisting, so each side resolves its own Effect. Your\n   application cannot even import efmesh — the dependency is not in its tree.\n2. **Data crosses the boundary, never live objects.** Your platform writes files\n   (parquet, csv, json) into the lake; efmesh reads them as `external` models,\n   builds marts, and writes files back. No `Effect`, `Schema` or `Layer` value\n   is ever passed across.\n3. **Drive it by CLI and read `--json` plus the exit code.** Spawn\n   `efmesh apply <env> --yes --json` from your orchestrator; `0` is success, `1`\n   is a failure, and `2` means a human is needed — a plan awaiting review, or a\n   `run` blocked by structural changes, with `blockedBy` naming the models. Pin\n   on the `apiVersion` field, not on the package version.\n\nNothing about efmesh needs to change for this: the isolation is a packaging\nproperty, and the machine-readable surface is the integration surface.\n\n## Performance\n\nThe framework overhead is negligible for any realistic project (in-memory DuckDB, `bun bench/plan-bench.ts N`):\n\n| models | cold plan | apply (all physical) | no-op plan | promote to prod |\n|---|---|---|---|---|\n| 100 | 54 ms | 158 ms | 3 ms | 51 ms |\n| 500 | 228 ms | 759 ms | 11 ms | 197 ms |\n| 2000 | 0.9 s | 2.9 s | 50 ms | 1.3 s |\n\n## Postgres\n\n```ts\nengine: { url: \"postgres://…\" },  // canonicalization via libpg_query\nstate:  { url: \"postgres://…\" },  // schema efmesh_state\n```\n\nBackfill runs batches in parallel (connection pool); independent DAG branches build concurrently. DuckDB federation (seeds, parquet, external files, export, ducklake) fails honestly on Postgres with `EngineFeatureError` — no silent degradation.\n\n### Support tiers\n\nTwo engines, two levels of coverage — stated plainly so you can judge the risk before you adopt.\n\n| Tier | Engine | State store | What the test suite exercises |\n|---|---|---|---|\n| **1** | DuckDB | SQLite (or Postgres) | Everything: all model kinds and targets, the parquet/DuckLake lake, seeds and `external` federation, audits, the janitor, `--forward-only` / `--reclassify`, `testModel`, and golden fingerprint freezing. |\n| **2** | Postgres | Postgres schema `efmesh_state` | State store (snapshots, promote/orphaning, intervals, ttl lock, `migrate`), libpg_query canonicalization, `describe`, and e2e `full` / `view` / `incrementalByTimeRange` backfill, `incrementalByUniqueKey` upsert and `scdType2` — with parallel batches and DAG concurrency. |\n\n**Not covered by tests on Postgres**, without hiding it:\n\n- **Structurally unavailable** — the DuckDB-federation surface: `target: \"parquet\"`, `target: \"ducklake\"`, CSV/JSON seeds, and `external` file/parquet/URL sources. These raise `EngineFeatureError` on Postgres by design; the suite asserts they *fail honestly*, never that they work.\n- **Works, but proven only on DuckDB** — audits (`notNull` / `unique` / `accepted`), the janitor, `--forward-only` / `--reclassify`, and `testModel` (which always runs on in-memory DuckDB, whatever your project engine).\n\n## Non-goals\n\nDecided, not deferred — the ready answer to \"why not just…\":\n\n- **A Node runtime.** efmesh is Bun-first to the core — `Bun.SQL`, `Bun.cron`, `bun test`, single-file config loading. Node would mean a second runtime matrix maintained for an audience we are not chasing; the target is TypeScript teams already on Bun.\n- **Multi-dialect SQL (transpilation).** sqlglot's killer feature, and we admit reproducing it in TypeScript is unrealistic. Dialect is a property of the *project*, not the model: you write for your engine (DuckDB or Postgres), and a `ref` typo stays a compile error either way.\n- **Cloud data warehouses** (Snowflake / BigQuery / Redshift). The whole thesis is small data lakes — DuckDB-class data, gigabytes to a terabyte on one machine. Cloud DWH is dbt/sqlmesh's home turf and carries the weight (adapters, infra) we deliberately shed.\n- **A third engine.** Each engine costs a full adapter *and* a canonicalization backend, and multiplies the test matrix. We would rather keep two engines honest — DuckDB tier 1, Postgres tier 2 — than three shallow.\n\nThe architectural non-goals (heavy ingest, general orchestration, BI) live in [SPEC.md](https://github.com/avytheone/efmesh/blob/main/SPEC.md) §1.\n\n## Status\n\n**0.5.0** (beta). The core is built and exercised on a live example: phases F0–F6 ([SPEC.md §13](https://github.com/avytheone/efmesh/blob/main/SPEC.md), [CHANGELOG](https://github.com/avytheone/efmesh/blob/main/CHANGELOG.md)), 288 tests including a live Postgres cluster and golden tests freezing fingerprint stability. Effect v4 is a beta dependency: pinned exactly (peerDependencies); a weekly CI job tracks drift against fresh betas.\n\nMaking efmesh legible, developable and operable by an AI agent — complete `--json` coverage with a pinnable `apiVersion`, in-repo and packaged skills, honest contracts — is done. Current work runs under the `dogfood: onto` theme: what a real deployment asks for once the pipeline runs unattended, which so far has meant telemetry, compaction, a lake a browser can read, and a run of work on not claiming more than the data supports: a passport that travels the DAG, audits that declare their scope, continuity gates that refuse with numbers. Work is grouped by theme rather than by version — which release a change lands in is decided when it is cut ([SPEC.md §11.1](https://github.com/avytheone/efmesh/blob/main/SPEC.md)) — and the themes come from dogfood needs rather than a fixed roadmap. Known limitation: a single `bun build --compile` binary builds, but standalone Bun executables can't resolve the `\"efmesh\"` import from a runtime-loaded config — distribution is via the package (SPEC §10).\n\n## Versioning\n\nThe major is `0` and stays there for a while: `1.0` will mean there is nothing\nleft to do, not that we started feeling serious. SemVer says nothing useful\nbelow `1.0`, so here is what we actually promise:\n\n- **a minor (`0.N.0`) may break** CLI flags, `--json` shapes, the public API\n  whitelist, the config shape or the model-definition surface — always as a\n  `BREAKING` bullet in the [CHANGELOG](https://github.com/avytheone/efmesh/blob/main/CHANGELOG.md) with its migration alongside;\n- **a patch (`0.N.M`) breaks none of those** — defect fixes, docs, internals,\n  performance;\n- **additive is minor, not patch**: a new flag or a new `--json` field is new\n  functionality, however small.\n\nWhat you actually pin on is not the package number but the contracts that carry\ntheir own versions: `apiVersion` in every `--json` payload, `STATE_VERSION` (a\nstore change ships an `efmesh migrate`), `FINGERPRINT_VERSION` (an algorithm\nchange re-plans as breaking changes and rebuilds on the next apply). Exit codes\nare frozen regardless of version. The full rule is [SPEC.md §11.1](https://github.com/avytheone/efmesh/blob/main/SPEC.md).\n\n## Documentation\n\n- [SPEC.md](https://github.com/avytheone/efmesh/blob/main/SPEC.md) — the architecture spec: decisions, invariants, open questions;\n- [CHANGELOG.md](https://github.com/avytheone/efmesh/blob/main/CHANGELOG.md) — release history;\n- [examples/hospital](https://github.com/avytheone/efmesh/tree/main/examples/hospital) — a live example with every model kind;\n- [examples/eventlake](https://github.com/avytheone/efmesh/tree/main/examples/eventlake) — the [canonical table](#event-lake-canonical-table) over an at-least-once event lake: dedup, typing, and the guarantee stated exactly;\n- [CONTRIBUTING.md](https://github.com/avytheone/efmesh/blob/main/CONTRIBUTING.md) — build, test and PR guide;\n- [llms.txt](https://github.com/avytheone/efmesh/blob/main/llms.txt) — a machine-oriented map of the repo for an evaluating AI agent.\n\n### Agent skills\n\nefmesh expects most of its *operation* to run through AI agents, so it ships\n[Claude Code skills](https://github.com/avytheone/efmesh/tree/main/skills) that\nteach an operating agent the safe procedures — each drives `--json` outputs and\n[exit codes](#exit-codes) only, never scraped text:\n\n- `efmesh-triage` — read `status --json` + the tick journal; tell awaiting-human\n  (exit 2) from lock-held from a real error, and what to do for each;\n- `efmesh-safe-apply` — preview `plan --explain --json`, then apply; when\n  `--reclassify` / `--forward-only` are appropriate and when they are forbidden;\n- `efmesh-backfill-recovery` — find failed/missing intervals and rerun with `run`;\n- `efmesh-environment-hygiene` — `diff` / `diff --data` before promotion, janitor\n  cadence, and what to back up;\n- `efmesh-upgrade` — bump the package, `efmesh migrate`, verify with `status --json`.\n\nWire them into your project by pointing your agent at the installed package —\n`node_modules/@avytheone/efmesh/skills/` — or copy/symlink the ones you want into\nyour project's `.claude/skills/`:\n\n```sh\nln -s ../../node_modules/@avytheone/efmesh/skills/efmesh-safe-apply .claude/skills/\n```\n\n## License\n\n[MIT](https://github.com/avytheone/efmesh/blob/main/LICENSE) © Alexey Yakimanskiy\n","readmeFilename":"README.md"}