All notable changes to this project will be documented in this file.
- ci-tools / Vercel: add first-class production-domain support to the shared
Vercel deploy path.
ci-tools deploy vercel --production-domain <host>now aliases production deployments to canonical custom domains, reports those domains in workflow-report evidence, and uses the canonical domain as the final URL for live verification. The devenv Vercel task module threadsdeployment.productionDomainsthrough toci-tools, so downstream repos can keep custom domains in generated deploy-surface config instead of one-off dashboard state. - devenv / genie: replace the shared Genie task module's raw
_module.args.geniePkg/_module.args.genieInputGlobscontract with declaredeffectUtils.genie.packageandeffectUtils.genie.extraInputGlobsoptions. This keeps packaged Genie consumption on a stable module API and lets downstream repos import the module without compatibility shims. - genie / bootstrap: narrow the packaged
genie-bootstrap-closure-checkderivation to the checker entrypoint, its first-party runtime sources, Bun, and the TypeScript tarball declared by@overeng/genie. The checker now walks source-tree.genie.tsfiles directly instead of shelling out to Git, and no longer goes through the rootmkPnpmCli/pnpm fixed-output dependency surface, keeping the bootstrap proof boundary separate from the normal Genie runtime. - genie / bootstrap: make the shared
bootstrap-closure:checkdevenv task run a packaged effect-utils checker against the importing repo root via--root, so downstream repos can reuse the zero-tolerance bootstrap gate without carrying local wrapper scripts or relying on ambient Bun/package-manager module resolution. - notion-cli:
schema generate <id> -o schema.gen.tsnow writes the file again. Thegeneratecommand registered both a file--output/-ooption and the shared TUI render-mode option, which also claimed--output/-o, so the render-mode choice validator shadowed the file path and rejected it. The TUI render mode ongeneratenow uses a distinct--output-modeflag (no-oalias), leaving--output/-ofor the output file. Added a parse-level test asserting-oresolves to the file path. (introspect/diff/generate-configkeep the shared render-mode--output/-o; they have no file option to collide with.) - megarepo / CI: refresh the nested
effectlock to the reachable upstreammaincommit so downstream coldmr apply --alljobs no longer fail before tests with an unavailable locked commit. - ci-tools / genie: fix the mechanically-fixable bootstrap-closure violations
at the source (this drove the interim baseline from 79 entries down to the 5
genie/weaver-registry/*.genie.tsresidual; the baseline is then removed entirely by the generator-phase change below). The wide@overeng/ci-toolsbarrel (src/mod.ts)export *ed the runtime./deploy-*encoders alongside the bootstrap-safe workflow-report constants/types, so genie helpers importing the safe symbols draggedeffectinto their bootstrap closure (73 violations); the safe surface is extracted into a new dependency-freeci-tools/src/workflow-report.ts(re-exported bymod.ts, so runtime consumers are unaffected) and the genie helpers repointed there. Separately,tsconfig.all.json.genie.tsimported the wide@overeng/genie/nodebarrel (which re-exports thetypescript-importing closure walker), now narrowed to the bootstrap-safenode/tsconfig-from-packages.tsmodule (1 violation). The 5 weaver-registry generators reacheffectbecause the OTel semconv contracts are Effect-Schema by design and render post-install; decision 0004 classes themdesign-time(out of the bootstrap-closure scope by declaration, not baseline). - CI / cargo: move standalone Rust crate build/test/clippy/fmt semantics into
the
cargo:checkdevenv task and make the generated cargo CI lane call that task, ensuring thenode-cpuprofileintegration test runs with the devenv Node toolchain instead of a Rust-only CI PATH. - devenv / lint: mark the
check:*:tracewrappers as intentionally rawotel-runentrypoints for the trace audit, and apply the repo formatter to the OTEL/Vitest context docs. - devenv / lint: make
devenv:trace-auditself-contained by referencing its Nix-provided scanner and marker-check binaries instead of relying on ambientrg,head,tail, orgrepinPATH.
-
genie / weaver: TS constants generation now exports prefixed
METRIC_*andSPAN_*name constants, plusMetricNameandSpanNameunions, so telemetry producers can import generated names without colliding with existing unprefixed attribute-key constants. -
genie / check: bootstrap-safe import-closure gate (
bootstrap-closure:check). A.genie.tsand everything it transitively imports at RUNTIME must be importable from a fresh checkout BEFORE install; a generator that reaches a runtime-only package (e.g. through a wide barrel thatexport *s a module importingeffect) breaksgenie:runon a fresh clone. The sharedcheckBootstrapClosurewalker (reusing TypeScript's parser/resolver plus genie's own#/#mrresolution, and excluding type-only edges) is exported from@overeng/genie/node; a bun entry (genie/ci-scripts/bootstrap-closure-check.ts) runs it over the tracked// @genie-bootstrap-marked.genie.tssources with ZERO TOLERANCE (no baseline, no allowlist —design-timegenerators are out of scope by declaration; see the generator-phase entry below). Wired intocheck:all(notcheck:quick). The walker imports genie's resolver via the effect-freecore/import-map/sync-resolver.ts(onlynode:fs/node:path), so it is itself importable pre-install (typescript+ node builtins only) and usable by nix-packaged-genie downstream members. -
genie / bootstrap: generator phase + empirical cold-proof (decision 0004, issue #884). Each
.genie.tsdeclares its phase with a valueless// @genie-bootstrapflag comment (mirroring@ts-nocheckgrammar;design-timeis the default, no marker), read statically without importing the generator.genie --phase bootstraprestricts a run to the marked set (the 35package.json.genie.ts+pnpm-workspace.yaml.genie.ts). Bootstrap-safety is now demonstrated, not asserted:bootstrap:cold-proof(genie/ci-scripts/bootstrap-cold-proof.sh, devenv task + CI lane) builds the self-contained packaged Genie CLI (.#genie, deps baked into the store), runsgenie --phase bootstrapin a freshnode_modules-freegit archivetree, thenpnpm install --frozen-lockfile, asserting both succeed and that the marked set actually ran.bootstrap-closure:checkstays as fast local feedback incheck:all. Supersedes the interimgenie:bootstrap-before-pnpm:installtask edge, which arbitrated nothing (source-mode genie can't run cold; committed outputs satisfy install regardless) and is removed. -
devenv / otel:
otel-run— atime-like wrapper that runs any command under a fresh root trace and prints its Grafana URL. Derives the root label from argv (devenv tasks run X→X), mints a fresh trace id (--jointo nest in the ambient trace instead), and probes for a reachable OTLP endpoint (the configured one, then the local ingress) so spans actually land instead of hitting a dead devenv-local collector. Pluscheck:quick:trace/check:all:tracetasks (otel-run devenv tasks run check:quick|all) — so getting a trace URL for a check run is a one-liner. -
otel-scrape:
deadnixadapter (src/adapters/deadnix.rs) — a diagnostics adapter overdeadnix --output-format json(NDJSON), mirroring oxlint. Dead-code findings become public-safe events (hashed filename + line; symbol names, paths, and columns never reach a sink) plus adeadnix.findingscount; otel-scrape re-renders a human summary. Wired ontolint:nix:deadcode. Added purely as a module + registry entry (nolib.rsdispatch change), exercising the adapter framework. -
otel-scrape: adapter fleet VRS under
context/otel-scrape/adapters/— the supported/candidate matrix, per-adapter requirements+spec leaves (oxlint, pnpm, deadnix, nix, vitest, node-cpuprofile) with source-of-truth references, and the fleet audit decisions. -
devenv tasks:
lint:check:format(oxfmt) andlint:nix:format(nixfmt) opt into otel-scrape asadapter="none", so each emits a timed, named command span beneath its task span when traced (bare otherwise). Behavioral testotel-scrape-oxfmt-wrap.test.shcovers themkLintExecnested-shwrap. -
otel-scrape: Root trace surfacing (decision 0020, R31). When
otel-scrapemints the trace root and telemetry is active, it prints the trace identity to stderr at end of run so agents and humans can open or correlate the trace without querying the backend first. With--trace-url-template <tmpl>/OTEL_SCRAPE_TRACE_URL_TEMPLATE(a backend-agnostic{traceId}placeholder template) and a successful export it prints a resolvable link; otherwise it prints the baretrace:<id>. OSC 8 hyperlink on a TTY, plain text when piped. Terminal-only — never written to the summary or OTLP sinks. Bound to export success, not the child exit code. Pure passthrough stays silent (R04);--trace-link off/OTEL_SCRAPE_TRACE_LINK=offdisables it. -
genie semantic-conventions generator (M1) / @overeng/genie + @overeng/otel-contract: First genie generator for OpenTelemetry semantic-convention registries.
- Layer 1 (
@overeng/geniesrc/runtime/weaver, dep-free): a faithful typed model of the Weavergroups:registry + deterministic renderers (manifest / attributes / signals YAML, TS + Rust name constants) using genie's own YAML stringifier.registryFromMembers+orphanSeamPathsland in@overeng/genie/composition. - Layer 2 (
@overeng/otel-contractnew./registrysubpath): Effect-Schema authoring (attr/span/metric/operation,defineOtelContract, the AST→fragment projector) atop the runtime primitives, with derived namespace + catalog,refExternalforeign-ref marking, andSchema.TaggedErrorauthor-time validation. Verified out of the runtime.bundle. - Weaver gate:
weaver:checkdevenv task (wired intocheck:all+ a dedicated CI lane) runsweaver registry check --future(pinned from-source flake, v0.24.2) against the emitted registry, resolving the pinned upstream OTel semconv (v1.37.0) hermetically via a Nix FOD. Block-vs-degrade: validation failures block; weaver unavailability degrades to a warning. - Provenance: per-file input fingerprint (sha256 over registry source + generator + pinned weaver + pinned upstream semconv), split so doc-only edits re-hash only the YAML outputs.
overeng/otel-contract-in-seam-fileoxlint rule (WARN-only) + the no-orphan-seam aggregator check make contract discoverability structural (decision 0005).
- Layer 1 (
-
genie semantic-conventions Rust target (M2) / @overeng/genie: Finalized the Layer-1 Rust emitter (
renderRustConstants) for Rust telemetry producers (decision 0007). Emits idiomatic, deterministic (sorted), rustfmt-clean const modules — separateattribute/span/metricmodules (collision-proof by construction) with apub const <SCREAMING_SNAKE>: &strper name plus anALL: &[&str]slice — covering own attribute keys, span ids, and metric names. Wired one committed.rsoutput (genie/weaver-registry/constants.rs) viaconstants.rs.genie.tsalongside the TS constants, with a dedicated Rust-identity fingerprint (attr keys + span ids + metric names) so a signal rename re-hashes only the.rsand a doc-only edit churns neither binding. Proven by a synthetic ~25-nameotel_scrape.*fixture (authored via the real Layer-2 seam) with a test asserting determinism, full name coverage, and valid/rustfmt-clean Rust (rustc + rustfmt, skip-if-absent). -
genie semantic-conventions first-namespace migration (M3) / @overeng/genie: Migrated genie's OWN telemetry (the
genie.*namespace, 12 attribute keys + 8 span operations) fully onto the registry seam. A newpackages/@overeng/genie/src/core/genie.contract.tsauthors the catalog + operations via the Layer-2@overeng/otel-contract/registrysurface;src/core/observability.tsis re-pointed at the DERIVEDOtelOperationproduct APIs (SC-R13/R14) with emit behavior unchanged (same keys, encode, span names, and thegenie/commandroot span, preserved via.withRoot). Registered in the root aggregator'smemberSeamPaths; the composed registry now emitsgenie.attributes.yamlalongsideacmeand passesweaver registry check --future. Encoder equivalence is raised fromdeepStrictEqualto a property-based proof through the realOtelOperationsurface (@effect/vitestit.prop+Schemaarbitraries over the optional/label branches).overeng/otel-contract-in-seam-fileis flipped to ERROR for genie's telemetry paths (rest of repo stays WARN; staged per decision 0005). The dynamic-namecli.moderoot span (genie/<cliMode>, a foreigncli.*namespace) stays a documented legacy inline op — no stable single-signal projection — deferred to a futureclinamespace member. -
genie semantic-conventions evolution gates (SC-R11, SC-R12) / @overeng/genie: Two additive weaver gates alongside
weaver:check.weaver:diff(SC-R11) runsweaver registry diffagainst theorigin/main..HEADmerge-base baseline (materialized offline via git, upstream held constant) and blocks a PR that REMOVES a shipped attribute/signal; it is JSON-payload based (--format json, block onchanges.*[].type == "removed") becauseweaver registry diffexits 0 even for breaking changes. A rename recorded weaver-native — old key retained +deprecated: { reason: renamed, renamed_to }— surfaces astype: "renamed"and passes with no custom gate logic (decision 0009).weaver:live-check(SC-R12) captures registry-conformant OTLP emitted from a first-party site and assertsweaver registry live-checkaccepts it. Both degrade to a warning when weaver/git is unavailable (GEN-R09);weaver:diffjoinscheck:all,live-checkruns in the CIweaverlane only (subprocess/timing e2e). -
genie semantic-conventions rename representation (decision 0009) / @overeng/genie: Attribute renames use the weaver-native
deprecated: renamed_toform (retain the old key marked deprecated for a dated sunset window), empirically chosen over a drop-old-keydeprecatedAlias.wasshape becauseweaver registry diffclassifies the former as a nativetype: "renamed"and the latter as a breaking removal (.experiments/2026-07-03-weaver-rename-diff.md).
- otel-scrape: Export spans when
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufis requested instead of disabling export. The OTLP/HTTP receiver routes by requestContent-Type(not the client's protocol env), and/v1/tracesacceptsapplication/json, so otel-scrape now sends its JSON payload and prints anote:rather thantrace export is disabled.grpcstays disabled (a genuinely different transport this HTTP/JSON exporter cannot speak). This unblocks adapter spans reaching the collector in the fleet's protobuf-configured environments. - devenv tasks:
lint:check:formatno longer fails when run under tracing.mkLintExec's empty-selection swallow keyed on an exact stderr match, which the otel-scrape wrapper's own stderr line broke for an all-ignored batch. The swallow now keys on oxfmt's exit code 2 and the diagnostic substring — robust to any extra wrapper stderr — so a genuine parse error (exit 2, no diagnostic) and a formatting diff (exit 1) still fail. (Also corrects the old exact-match, which never matched real oxfmt's longer message.)
-
@overeng/notion-cli: Default generated Notion relation properties to
relationIdseven when Notion reportssingle_propertyrelation metadata. Explicit single-relation transforms remain available through transform configuration for schemas that intentionally require scalar cardinality. -
otel-scrape: Refactored the adapters into a per-tool module framework (
src/adapters/): aToolAdaptertrait +ADAPTERSregistry, with oxlint, vitest, and node-cpuprofile each in their own module.lib.rsdispatch is now registry-driven (adapter_for(...).map_or(...)) with no per-adapter-namematch; injection is dynamic viaprepare/AdapterPrephooks. Adding an adapter is now onesrc/adapters/<tool>.rs+ oneADAPTERSentry + registry JSON, so adapters can be developed and maintained in parallel. Pure behavior-preserving restructure —tests/cli.rsoutput byte-identical. -
otel-scrape / devenv: Make effect-utils' own devenv tasks cooperate with the trace model (decision 0018). The task span (
otel-span devenv.task.exec/devenv.task.status) owns the task level; the blanket per-taskotel-scrapewrapper is removed (it produced one meaningless genericbashspan above every real task span). A newtrace.instr { adapter ? "none"; name; }helper wraps a clean concrete command BENEATH the task span, yielding a named command span (tsgo,oxlint,node, ...). Adapters fire where the structured-source contract (decision 0017) is met: oxlint is instrumentedadapter=oxlintwith a presence-gated--format=json(otel-scrape re-renders a human summary), vitestadapter=vitest(side-channel; human reporter output preserved), and tsc/tsgoadapter=none(compiler-only wrap keeps the honesttsgospan name with the TypeScript phase spans as task-siblings). Thetrace.instrarrays are empty when otel-scrape is absent, so downstream repos importing these modules run unchanged. -
Refined
otel-scrapeVRS and release docs for helper-backed exact process observation, including Linux cgroup-scoped run authority, macOS Endpoint Security validation gates, and runner-class support-matrix evidence. -
genie semantic-conventions encoder-equivalence proof — consolidated / @overeng/otel-contract: Retired the 13 per-namespace
*.observability.equivalence.unit.test.tsbridges in favor of one strengthened mechanism proof over the./registryseam (registry.unit.test.ts+registry-equivalence.property.unit.test.ts), covering the union of encoder shapes (enum / template / optional / redacted / json / drop, metric-label rejection, requirement-level projection). Genuinely-unique non-equivalence assertions (genie's trimmedspan.label+ non-finite rejection) are preserved in a newgenie/src/core/observability.unit.test.ts. -
@overeng/megarepo tests: Disable Git auto-maintenance in the hermetic test gitconfig so detached
git maintenance run --autoprocesses cannot race scoped temp-directory cleanup on macOS CI. -
genie semantic-conventions registry — genie-idiomatic emit + relocation / @overeng/genie: Reworked the first-party registry to a builder-per-target API and moved it under
genie/.- New dep-free
weaver*builder family in@overeng/geniesrc/runtime/weaver(weaverManifest/weaverAttributes/weaverSignals/weaverTsConstants/weaverRustConstants) over aWeaverRegistryBundle(composed registry + the three split provenance fingerprints + whole-registry integrity issues). Each.genie.tsemitter is now a one-liner over the aggregator's exportedweaverbundle;weaverManifestsurfaces namespace-uniqueness / dangling-ref issues viavalidatesogenie:checkstill blocks. - Collapsed the per-namespace
<ns>.attributes.yamlemitters into a singleattributes.yaml(newrenderAttributesemits all groups as multipleattribute_groupentries in one file). Adding a namespace no longer needs a new.genie.ts. The fixed emitted set is now{manifest, attributes, signals}.yaml+constants.{ts,rs}. - Moved the registry directory
weaver-registry/→genie/weaver-registry/(updated theweaver:checktaskregistryDir, its YAML copy glob, the no-orphan-seam test path, the oxlint ignore glob, andREGISTRY_SOURCE). Emitted YAML/TS/Rust content is unchanged apart from theregistry-source:provenance-header path (fingerprints are identical).
- New dep-free
-
devenv deploy tasks / @overeng/ci-tools: Centralize deploy-preview workflow-report and GitHub output emission in
ci-tools, including provider-owned skip records, URL outputs, and task metadata, so generated CI delegates deploy behavior through devenv tasks. -
devenv deploy tasks / @overeng/ci-tools: Keep manual CI runs from failing the Netlify deploy job when no deploy branch runs, and capture verbose Vercel CLI output with an explicit buffer.
-
devenv deploy tasks / @overeng/ci-tools: Remove the obsolete Nix deploy metadata and alias helpers now that
ci-toolsis the single workflow-report record authority. -
devenv workflow-report tasks / @overeng/ci-tools: Add reusable
workflow-report:*devenv tasks for bundle collection, comment rendering, and PR comment publication, and regenerate CI to delegate deploy-preview reporting through those tasks. -
devenv deploy tasks / @overeng/ci-tools: Move Vercel build-mode
vercel pull/vercel buildorchestration, root-directory patching, temporary install-command overrides, and prebuilt output validation intoci-tools deploy vercel, leaving the devenv task as thin config delegation. -
devenv deploy tasks / Nix: Package the Netlify and Vercel provider CLIs as first-party fixed-output npm derivations (
netlify-cli26.1.0 andvercel54.18.5) and make shared deploy tasks default to those store paths instead of runtimebunx/stale nixpkgs CLI resolution. -
devenv deploy tasks / Nix: Trim the Netlify and Vercel provider CLI derivations by installing and pruning their npm package graphs without optional dependencies and skipping dependency rebuild scripts during packaging.
-
CI: Collapse guarded live Netlify and Vercel deploy E2Es into one shared live deploy job with common setup.
-
@overeng/genie: Validate documented GitHub Actions limits for static matrix expansion, check runs per check suite, and explicit job timeouts.
-
@overeng/genie: Fail generated GitHub workflow validation above the observed 512 KiB Actions admission limit and warn when workflows approach it, so oversized workflows are caught by
genie:checkbefore GitHub silently skips PR runs. -
genie/ci-workflow: Move the Nix transient-failure retry implementation out of generated workflow YAML and into checked-in CI scripts, keeping
runDevenvTasksBefore(...)as the Genie composition API while shrinking the generated CI workflow well below GitHub's admission limit. -
genie / GitHub rulesets: Require every non-advisory generated CI lane in branch protection, including default-ref, bundle smoke, cargo, CI measurement, and integration lanes, while keeping reporting-only jobs advisory.
-
devenv test tasks: Bound package test fanout while preserving package-specific prerequisites, so native-link setup is preserved without re-entering Devenv's upstream task graph in parallel.
-
devenv test tasks / @overeng/megarepo: Give the isolated cold-GC integration task explicit Vitest timeout headroom and per-test timing output for slow CI runners while keeping the package-wide default timeout unchanged.
-
nix packages / @overeng/genie: Refresh the Genie, Megarepo, TUI Stories, and Notion MD pnpm-deps fixed-output hashes and keep the native dependency policy audit install-free by splitting the policy into a lightweight module.
-
tests: Give repo Vitest tasks and
@overeng/ci-toolsprocess-spawning E2Es CI timeout headroom, and keep the pty-effect roundtrip fixture alive long enough for loaded Linux runners to attach reliably.
-
otel-scrape: Complete
span.clisemantic-convention conformance (decision 0016, M25.1): emit the REQUIRED rawprocess.pid(never hashed, not trust-gated); seterror.type=_OTHERand a bounded, non-sensitive spanStatus.message(process exited with code <n>/process terminated by signal <NAME>) on non-zero exit; boundprocess.executable.name/ the span name via a documented low-cardinality derivation (safe-charset basename, nix-store hash prefix stripped, pathological names collapsed to<binary>); derive span end time from a monotonic nanosecond delta so fast commands are no longer zero-width or whole-millisecond-quantized; and set the instrumentation-scopeversionplus a default resourceservice.versionto otel-scrape's crate version. -
otel-scrape: Correlate traces to a build and enrich adapter events (decision 0019, H5). Derive a
machineVersion(<version>+<rev>[-dirty]) from a flake-injectedCLI_BUILD_STAMP(following the shared build-identity contract in@overeng/utilscli-version) and emit it as the instrumentation-scopeversion, the default resourceservice.version, andtelemetry.sdk.version— superseding the bare crate version so a trace ties to a commit — and print it from--version. Addschema_url=https://opentelemetry.io/schemas/1.37.0on the resource and scope. The oxlint adapter event now carries the publicruleid andlinein both the OTLP and summary sinks (the filename stays hashed). The defaultservice.versionis gated onservice.namebeing otel-scrape's own default, so a user/harness--service-name(orOTEL_SERVICE_NAME) no longer has otel-scrape's build stamped onto its service identity. -
otel-scrape: Add the per-named-sink command-identity trust gate (decision 0015):
--trusted-sink otlp|summary(repeatable; env aliasOTEL_SCRAPE_TRUSTED_SINKpinned to the OTLP target only) emits rawcommand.argv/command.cwdinto the named sink alone. The default stays hashed-only and the local summary is hard-public-safe — an OTLP assertion never covers it. A byte-level non-leak regression test enforces the invariant. -
@overeng/content-address: Add filesystem-backed CAS primitives with descriptor-addressed writes,
cas:URI resolution, canonical JSON manifests, and validated manifest pins as local retention roots. -
otel-scrape: Add the initial Rust crate skeleton with passthrough command execution, W3C
traceparentroot-or-join propagation, stable hashed command evidence, file-only JSON summaries, flake/devenv wiring, and cargo CI gates. -
otel-scrape: Add a generated telemetry registry with a JSON source of truth and Rust/TypeScript projections for wrapper schemas, span names, attribute keys, and profile fields.
-
otel-scrape: Add the first real adapter slice for
oxlint --format=json, preserving stdout while recording diagnostic events and a generatedoxlint.diagnosticsmetric in summary evidence. -
otel-scrape / @overeng/otel-contract: Add CAS-backed profile artifact links, a reusable
OtelScrapeProfileLinkschema, and CLI support for writing profile blobs, run manifests, and pins under an explicit CAS root. -
otel-scrape: Add the
node-cpuprofileadapter, which enables Node/V8 CPU profiling for wrapped Node commands, validates produced.cpuprofileartifacts, stores them through the CAS lane, and emits sanitized profile-link evidence. -
otel-scrape / @overeng/otel-contract: Add a consumer-neutral E2E fixture that captures a real
node-cpuprofilewrapper run throughotelite, decodes the emitted profile link with the TypeScript contract, and resolves the linked artifact from CAS. -
otel-scrape: Document the current stable CLI surface, summary/export modes, adapter support policy, CAS artifact handoff, privacy guarantees, and current support matrix, including the explicit non-claim for release-grade descendant process-tree spans until exact backend validation lands.
-
otel-scrape: Add the public VRS for an effect-utils-owned process wrapper that turns build/dev tool executions into OTEL spans, events, metrics, and content-addressed profile links.
-
otel-scrape: Add explicit degraded direct-child process observation evidence in summary JSON and OTLP export, including a validation fixture that proves descendant workloads are not reported as release-grade process trees.
-
otel-scrape: Add an opt-in Linux
ptrace-experimentalprocess backend that observes fork/vfork/clone, exec, and exit events for a traced child tree and validates exact descendant evidence with a compiled process-DAG fixture. -
otel-scrape / devenv: Wire
otel-scrapeinto effect-utils devenv task execution, status checks, Storybook processes, Netlify/SecretSpec tasks, and aggregate gates via the sharedtrace.nixtask wrapper. Local task runs now write summary evidence undertmp/otel-scrape/summariesby default andcheck:*runs a source audit to catch new unwrapped task scripts. -
otel-scrape: Document the adapter admission policy and exact process helper boundary. New build-tool adapters stay rejected until they land as a vertical slice with a structured source contract, passthrough preservation, privacy and degraded-mode tests, classification justification, generated contract updates, and consumer evidence for cross-package/profile contracts. Default exact process-tree support remains helper-gated rather than ptrace or sampled snapshots.
-
otel-scrape: Add the wrapper-side
helper-streamprocess backend contract, including--process-helper-socket/OTEL_SCRAPE_PROCESS_HELPER_SOCKETparsing and fail-closed degraded evidence until a validated privileged helper stream proves exact lifecycle coverage. -
otel-scrape: Implement the
helper-streamNDJSON parser and fake-helper E2E contract tests. Exact process evidence now requires a complete, same-run, version-matched lifecycle stream; helper loss, sequence gaps, run-id mismatch, version mismatch, timestamp/order violations, multiple roots, disconnects, and incomplete lifecycle data degrade explicitly. -
otel-scrape: Support the official OpenTelemetry trace-export environment variable surface for the first-party OTLP/HTTP JSON exporter, including trace-specific endpoint precedence, resource attributes, service-name precedence, headers, timeout, SDK/exporter disable flags, and explicit unsupported-protocol handling.
-
@overeng/utils-dev / @overeng/megarepo: Add reusable
otelitetrace/metrics/log diagnostic JSON writers and make store fixture setup emit typed OTEL spans through the production bounded git runner, so CI fixture stalls can produce inspectable trace evidence. -
@overeng/genie: Document the intended package export type-proof architecture: strict proofs should use an explicit compiler executable boundary (
tsgoby default, custom only by explicit override) instead of dynamic TypeScript module imports or stagednode_modulesplumbing. -
context/ci-tools: Start the issue #868 VRS kickoff by restoring the non-protected CI tools spec/glossary/source-of-truth decision and recording a Phase 0 deploy-preview inventory note for workflow-report, Netlify, and Vercel migration surfaces.
-
@overeng/workflow-report: Add a hermetic CLI E2E test that runs the real
workflow-reportentrypoint through Bun, collects marked deploy-preview records, renders managed comment output, and finds the managed comment ID through file-based CLI boundaries. -
devenv deploy tasks: Add fake-provider E2E coverage for the generated Netlify and Vercel deploy task scripts, proving PR aliasing, task output metadata, workflow-report record emission, Vercel prebuilt static packaging, Netlify diagnostic behavior, malformed provider output failures, missing PR input failures, and missing local static output failures without real provider credentials.
-
@overeng/ci-tools: Hard-rename the workflow-report CLI package and Nix flake output to
@overeng/ci-tools/#ci-tools, keeping the report helpers under theci-tools workflow-report ...command surface and regenerating CI to call the new binary boundary. -
@overeng/ci-tools: Add the first deploy domain model with versioned Effect schemas for deploy inputs/results, tagged deploy failure taxonomy, retryability derivation, redacted workflow-report record builders, and privacy/cardinality-safe OTEL span attribute metadata for deploy operations.
-
@overeng/ci-tools: Add the Phase 3 Netlify provider adapter with typed command-runner boundaries, schema-decoded deploy JSON, guarded live E2E aliases, redacted failure classification, and fake-runner coverage for unauthorized, missing-project, malformed-output, and aliased deploy paths.
-
@overeng/ci-tools: Add a guarded live Netlify E2E that deploys a local static fixture through
ci-tools deploy netlify, verifies marker content on the served alias, records cleanup status, and runs in CI only when Netlify secrets are available. -
@overeng/ci-tools: Add the Phase 4 Vercel provider adapter with API-first project diagnostics, local Build Output API static packaging, prod/PR/preview alias semantics, redacted failure records, fake-provider E2E coverage, and a guarded live E2E that verifies served marker content through Vercel's automation protection-bypass header and records best-effort alias cleanup.
-
devenv deploy tasks / @overeng/ci-tools: Move shared Netlify and Vercel deploy tasks onto thin
ci-tools deploy ...launchers, add task-output environment metadata for deploy URLs, preserve Vercel build-output deploy mode, and cover the task scripts through real-CLI E2E fixtures with fake provider endpoints. -
context/ci-tools / @overeng/ci-tools: Restore the CI tools vision and requirements VRS documents, update the spec for the current task-output, CLI-output, and provider-boundary contracts, and move provider API lookup plus live verification HTTP calls onto the Effect platform
HttpClient. -
@overeng/genie: Add the explicit
@overeng/genie/compositionsubpath for reusable cross-artifact composition helpers. The first helper,tsconfigReferencesFromPackages, projects TypeScript project references from package workspace metadata while keepingtsconfigJson(...)as a thin artifact builder. -
@overeng/megarepo / CI: Isolate the cold named-branch GC integration matrix into its own CI task and add deterministic git subprocess timeouts with OTEL
git.timeout_msspan attributes so stuck GC probes fail with the offending command instead of Vitest's generic per-test timeout. -
@overeng/megarepo / devenv tasks: Add an explicit
--lock-syncpolicy tomr apply/mr fetch --apply, including--lock-sync offfor non-mutating workspace materialization, and add a sharedmr:setupdevenv task that applies committed root members without fetching remotes or rewriting lock files.mr:checknow depends onmr:setupand points missing-member repair hints at setup instead of the lower-levelmr:applytask. Source-mode repo wiring now ordersmr:setupafter package installation like the other megarepo tasks. -
@overeng/genie: Add package-json-owned export environment contracts for JavaScript package entries.
exportEntry(...)attaches non-emitted runtime and type-level environment metadata to generated package exports, while the package-json validator owns the Node-only static import/global scanner, strict TypeScript proof, and cache-backed fast path for constrained environments such as Node, browsers, Workers, Workerd, and React Native. Contracts now cover the repository's package exports, including conditional browser/node entries and patterned source exports, replacing the previous one-off Genie entry-purity test. -
@overeng/genie: Add an opt-in package-json export contract coverage policy.
definePackageJson({ validation })can now bake warning/error pressure into a repo's package-json generator, while per-call options remain available as overrides. The policy reports package exports that are not annotated withexportEntry(...), supports glob ignores for staged migrations, reuses Genie validation warnings, and stays package-json-owned so shared repos can migrate toward JavaScript environment contracts without adding Genie core semantics. effect-utils' internal Genie surface now uses the configured generator in warning mode. -
@overeng/genie / CI: Add the
githubWorkflowEvent.alltrigger sentinel so GitHub workflow generators can request an unfiltered event without rawnullin authoring code. The main CI workflow now uses it to run for pull requests targeting any base branch, while keeping push CI restricted tomain. -
@overeng/megarepo, @overeng/tui-stories: Refresh stale fixed-output dependency hashes after the stacked PR workflow/API change altered prepared dependency closures.
-
devenv/lint-oxc: Make
lintPathsthe single lint surface contract. oxlint/oxfmt tasks now enumerate tracked plus untracked non-ignored files viagit ls-filesand always run instead of delegating change detection to devenv'sexecIfModifiedglob walker. The shared lint module is kept repo-agnostic; effect-utils-specific source policy checks live in local task modules. Per-tool file filters include the broader Oxc-supported extension sets, including.mts/.ctsfor oxlint and additional oxfmt-supported formats such as JSON5, SCSS/Less, MDX, GraphQL, and Handlebars. -
devenv task-module tests: Add a dedicated
devenv-modules:testtask for shell tests colocated undernix/devenv-modules/tasks/shared/tests, and wire it intotest:runso shared task-module regressions run in the normal CI unit test job. -
@overeng/notion-effect-client: Add
NotionMarkdown.markdownToBlocks, an AST-based selected-GFM Markdown importer that emits Notion append/create block payloads for paragraphs, headings, dividers, lists, task lists, code, quotes, and tables. The importer shares the same Notion-flavored remark/GFM setup as canonical Markdown so downstream packages can delete hand-rolled Markdown parsers and consume one block-payload contract.
-
otel-scrape: Preserve the wrapped command's exit code when optional summary file writing fails after the child process exits, while still reporting the summary write failure on stderr.
-
@overeng/content-address: Require manifest pins to target stored canonical JSON manifest bytes and allow safe pin path segments that merely start with dots, while still rejecting real parent segments.
-
CI: Keep the standalone Nix retry helper aligned with the generated workflow helper for missing daemon-socket failures, and cover the daemon retry path in helper tests.
-
@overeng/pty-effect: Stabilize the daemon env override live test on macOS by keeping the short-lived PTY session alive long enough for peek/attach assertions.
-
@overeng/genie: Emit Starlark
#generated-file headers for Buck2BUCK,.bzl, and.bxloutputs. -
@overeng/genie: Keep peer repo Genie authoring helpers from importing engine-only validation internals by sourcing repo-context helpers directly instead of through the broad node runtime entry.
-
@overeng/genie: Run strict package export type proofs through an explicit compiler executable, with Nix-packaged Genie wired to the pinned
tsgobinary and source-mode validation discoveringtsgoonPATH.lib.mkCliPackagesnow threads the same pinned compiler into the Genie package, and missing compiler executables are reported as validation issues instead of crashing cache-key computation. -
@overeng/genie: Tighten package-json export environment validation so constrained profiles reject bare Node builtin imports, follow extensionless directory entrypoints, and resolve overlapping conditional exports in emitted condition order.
-
@overeng/genie: Ship
typescriptas a runtime dependency so the CLI's JSONC validation path resolves outside the repo development shell. -
@overeng/genie / devenv tests: Refresh the pnpm fixed-output hashes for the new Genie
./nodeentry closure and the follow-up runtime dependency lockfile change, and isolate thets-otelitee2e test from ambient task trace context sodevenv-modules:testremains deterministic when run under traceddevenv tasks. -
devenv/lint-oxc, CLI package builders: Treat an
oxfmtchunk that becomes empty after formatter config ignores as no work while preserving failures for real formatter errors, and generate packaged CLI completions without assuming every CLI accepts the shared--log-level noneoption. -
@overeng/megarepo: Share canonical nested-megarepo traversal state across recursive commands and key visited roots by resolved worktree identity, so
mr status --allandmr ls --allstop at symlink cycles instead of recursing through ever-growingrepos/paths. -
devenv/pnpm: Keep the cached
pnpm-install-contract.jsonwritable even when the generated source file is read-only, so repeatedpnpm:installruns can update.devenv/task-cacheinstead of failing on the preserved generated file mode. -
Bundle smoke CI gate: Add a Vite/Rollup-based smoke check for
@overeng/pty-effectpublic entries so bundler-resolution ESM export mismatches in runtime dependencies fail in CI before downstream consumers hit them. Markpnpm-install-contract.jsonas generated in Git attributes. -
devenv/lint: Add a local
lint:check:asset-import-needs-type-referencetask (wired intolint:check) that fails when a compiled, exported source file imports an asset (import '...css|scss|sass|less') without a/// <reference>directive to make the ambient*.cssdeclaration travel into downstream TS checks (TS2882, #837). It runs out-of-band over tracked + untracked source (an in-oxlint rule would itself be suppressible by anoxlint-disable, which is how this regressed), so no disable directive — next-line, same-line, or file-level — can bypass it. Exempts the globs where side-effect imports are allowed and not type-checked downstream (.storybook/**,*.gen.*,*.d.ts,*.test.*,*.stories.*). -
Dependency materialization VRS hierarchy: Move the reusable pnpm install, projection, Nix prepared-deps, store-authority, Buck2 evidence, and observability contracts into
context/dependency-materialization/as a composed VRS hierarchy. The root DMP contract now stays mechanism-agnostic, while child specs own live pnpm state,.binprojection, prepared FOD purity, hash evidence, native package boundaries, shared-store repair/GC, and build-log producer facts. The hierarchy now includes a verification subsystem with a timeless evidence-intake contract for graduating prototype, downstream, CI, and historical change evidence into effect-utils-owned fixture, proof, real-workload, and cross-system evidence tiers. Imported research references now live in self-contained dot-prefixed companion directories instead of the normative spec. The VRS now records that the strict prepared-deps purity scan transition uses one convergentv18prepared artifact version bump and hash refresh, rather than report-only or profile-gated legacy scan modes, and that FOD run evidence belongs to generated repair or CI output rather than committed per-target witness files. The FOD hash evidence spec separates structural proof from value proof so shared hashes remain preferred only when measured outputs converge. -
Dependency materialization profile contract: Extend the generated
pnpm-install-contract.jsonwith a stabledependency-materialization-profile/v0section covering identity inputs, store traits, native build policy inputs, and the Buck2 boundary. The pnpm helper tests now prove profile emission and refuse raw pruning for sharedstore/v11/filespools, forcing coordinated repair plans instead of one-worktree cleanup. Successful livepnpm:installruns also emit profile and registry evidence in the install cache so future doctor/repair tasks can reason from registered materialization roots instead of probing pnpm's private store layout directly. A shared aggregate registry tracks all roots that point at the same files pool, while newpnpm:doctor,pnpm:repair-plan, and explicitpnpm:repairtasks consume that evidence to refuse raw pruning of shared files pools and force-rebuild every live registered root instead. Repair planning now refreshes from the shared registry, profile changes replace stale root rows, and repair execution uses the same pure install policy aspnpm:install. Prepared-deps profile keys now include content freshness digests for staged manifests and inherited root patch authority, andmk-pnpm-cliexposes a Buck2-facing evidence adapter that consumes the same prepared-deps profiles without owning live pnpm materialization. It also exposes generated FOD hash repair targets so repair tooling can discover direct prepared-deps attrs and hash paths without adding per-package witness files. -
pnpm install contract proof: Add a generated
pnpm-install-contract.jsonartifact that makes the long-term pnpm/Nix/Buck2 install contract explicit: pnpm ownsstore/v11/{files,links,projects}, GVSlinksare treated as a rebuildable dependency-graph projection, fixed-output Nix dependency prep does not consume live GVS projections, and Buck2 integration should key from the generated contract plus the lockfile rather than from pnpm's privatenode_moduleslayout.pnpm:installnow hashes the structuredgvsLinkContractJSON section for GVS relink invalidation instead of parsing renderedpnpm-workspace.yaml, and helper tests cover the prior false positive where policy-only changes were classified asgvs-linkdrift. -
@overeng/oxc-config native
overeng/storybook/*rules: Reimplement the seven Storybook CSF best-practice rules (meta-satisfies-type,default-exports,story-exports,csf-component,hierarchy-separator,no-redundant-story-name,prefer-pascal-case) as nativeoverengoxlint JS-plugin rules instead of re-exporting them fromeslint-plugin-storybook. This restores the enforcement temporarily removed in #804 and permanently drops theeslint-plugin-storybookdependency (the only remaining consumer of that catalog entry). Each rule carries a source-of-truth reference to its upstreameslint-plugin-storybook@10.4.6rule plus resync notes, and the now unreferencedeslint-plugin-storybookandoxfmtcatalog entries are removed. Intentional deviations from upstream (scope-analysis-dependent autofixes are detection-only;hierarchy-separator's|→/fix is kept) are documented per rule. -
Clean devenv OTEL task model (#377):
dt, devenv task wrappers, shell entry, pnpm cache-miss status spans, and TypeScript diagnostics now use a singleservice.name=effect-utils-devenvresource with stable operation span names (dt.run,devenv.shell.entry,devenv.task.exec,devenv.task.status,typescript.project.check,typescript.build.aggregate). Task identity moved to typed span attributes such astask.name,task.phase, andtask.cached; raw forwardeddtarguments are no longer recorded.ts:check/ts:buildemit typedtypescript.*,compiler.*, anddiagnostics.*attributes throughotel-span emit-spanfor bothtsgoand JStsc, with spool-only delivery and task-context propagation covered. The dashboards now query operation span names plusspan.task.name/span.ts.project.name, and the newts-otelite-e2e.test.shproof covers the fulldt.run -> devenv.task.exec -> typescript.*tree through real otelite. -
native dependency policy audit: Add
nativeDependencyPolicy, a tagged source-of-truth classification ingenie/external.tsfor every native npm dependency family (nix-grafted, denied-lifecycle-build, pure-package-artifact). The generated pnpmallowBuildsdenylist is now derived from it so the denylist and audit cannot drift. A new install-free CI audit (genie/ci-scripts/native-dep-policy-audit.ts, wired into thepnpm-builder-contractjob) diffs the lockfile against the policy and fails on drift: a CPU/OS/libc-gated native family with no policy entry, a non-defensive policy package that disappeared, or a nix-grafted entry whoseviafile is missing. Because pnpm lockfile v9 droppedrequiresBuildand the builder-contract job restores nonode_modules, the pnpm build-script ledger is unavailable; detecting a brand-new lifecycle-built package that is neither gated-native nor in the policy is therefore out of scope, with the policy as the source of truth (#807).
-
@overeng/react-inspector: Omit absent schema tooltip and lineage metadata instead of materializing optional fields as
undefined, so downstreamexactOptionalPropertyTypeschecks can compile the source-linked package. -
@overeng/notion-effect-client: Preserve Markdown importer semantics for soft-wrapped paragraphs, common JavaScript/TypeScript code-fence aliases, and loose list item child blocks when producing Notion append/create payloads.
-
Patch projection scope: Keep the
@myobie/ptyxterm serialize patch in effect-utils' root workspace config only, so downstreamcreatePnpmPatchedDependenciesconsumers do not inherit a pty-only patch and trip pnpm's unused-patch validation. -
@overeng/tui-react: Fix downstream
TS2882forTuiStoryPreview'simport '@xterm/xterm/css/xterm.css'. The*.cssambient that types the side-effect import is now referenced from the importing file via a/// <reference path>directive (to a shippedsrc/storybook/asset-modules.d.ts), so the declaration travels into every program that compiles the file — including downstream source-linked TS checks. Previously the ambient lived in floating.d.tsfiles loaded only by this repo's own tsconfigincludeglobs, so it did not ride along when a downstream consumer compiled the source viaexports-resolution. Removes those floating declarations (types/css.d.ts,tui-react/src/types/css.d.ts) and theirincludereferences across thegenie,megarepo,notion-cli, andtui-storiestsconfigs. The bundler still injects the stylesheet natively — no runtime or codegen change.
-
devenv/tasks: Remove the
dttask wrapper and make nativedevenv tasks run <task>the only task entrypoint. Task tracing now usesdevenv.task.exec/devenv.task.statusas the canonical span shape, dashboards are renamed todevenv-*, and guard passthrough now uses the task-neutralDEVENV_TASK_PASSTHROUGHenvironment variable. -
@overeng/restate-effect: Make the Restate real-server test harness use request identity and structured SDK log capture. The harness now generates an ephemeral ED25519 request-identity keypair per native-server boot, starts
restate-serverwith the private key, and serves each SDK endpoint with the derivedpublickeyv1_...identity key, removing unauthenticated-handler warnings at the source.EndpointOptions.sdkLoggerthreads a structured SDK logger intocreateEndpointHandler({ logger }), and the harness captures SDK SYSTEM/JOURNAL/USER records throughharness.sdkLogs.records()instead of relying on stderr orRESTATE_LOGGING=ERROR. Expected terminal domain failures remain available asTerminalErrorlog params with code/metadata, so the bridge keeps terminal/retryable/defect semantics intact while making integration test output quiet and inspectable. -
@overeng/restate-effect: Route the scheduled durability SIGKILL test's SDK endpoint diagnostics through structured log capture as well. The test now keeps the expected HTTP/2 abort evidence from killing
restate-servermid wake-mode wait, while avoiding misleading raw stderr noise in CI. -
Restate integration port allocation: Bind in-process handler endpoints on port
0and register the actual kernel-assigned URL, while keeping batch + retry allocation for the nativerestate-serverchild process ports that must be passed by number. -
CI test task ordering: Make shared devenv task-module tests wait for
pnpm:installso source-mode CLI shell tests cannot race dependency materialization undertest:run --mode before. -
CI alignment notification: Keep the
notify-alignmentdispatch limited to trusted push events so fork pull requests withoutMEGAREPO_ALIGNMENT_TOKENdo not run the coordinator dispatch job and fail with GitHub401. -
CI measurement reporting: Skip PR comment publication for fork pull requests that cannot write comments/assets, while keeping measurement summaries and artifacts. Chart asset fallbacks now re-render the comment from structured URLs instead of editing Markdown with brittle
sedranges. -
Genie compiled import staging cleanup: Scope compiled-binary
.genie.tsimport staging directories to the dynamic import lifetime so successful and failing compiled runs no longer leavegenie-import-*directories in the process temp directory. Add a compiled Genie CLI shell regression test that runs against an isolatedTMPDIRand verifies the staging directory is removed after repeated runs (#824). -
Effect-LSP diagnostics are now a strict quality gate (#811): The
@effect/language-serviceplugin config ingenie/external.tsis an explicit, intent-revealing gate policy driven by a singleeffectDiagnosticsGatetoggle, now ENABLED: an Effect warning OR suggestion anywhere in the workspace fails thetsgo --buildexit code (errors always gate). The gate rides the existing type-check over the project graph — no extra compiler pass — so it is enforced byts:check/ts:check:strict, hencedt check:quick/dt check:alland the CItypechecklane;--forceis not required (Effect diagnostics replay from.tsbuildinfo). To enable it, all 822 pre-existing Effect diagnostics (389 warnings + 433 suggestions) were burned down to zero with idiomatic, behavior-preserving fixes (typed error channels viaSchema.TaggedError,Schema.parseJsonover raw JSON, singleEffect.provide/correctLayerconstruction,Effect.orElseSucceed/Effect.void/Schema.TaggedStruct, etc.); a small number of genuinely un-nameable sites (framework dynamic-dispatch boundaries, type-level negative-assertion files) carry narrow justified@effect-diagnostics … :off/:skip-filewaivers.missingEffectContext/missingEffectErrorkeep their upstream defaulterrorseverity; the deadreportSuggestionsAsWarningsInTscflag was dropped (it does not exist in tsgo / LS 0.86.2). Alignment heads-up: this is the sharedbaseTsconfigCompilerOptions, so peer repos that consume it inherit the gate on their next effect-utils bump; a repo not yet clean can locally setignoreEffect{Warnings,Suggestions}InTscExitCodeback totrueuntil it converges. -
OTEL trace-structure contract: Tighten the offline trace-structure negative test so orphan detection uses a syntactically valid but missing parent span ID. Invalid span IDs are now left to the helper's input validation path, keeping graph-shape assertions separate from argument validation.
-
devenv cli-guard ownership: Make the cli-guard nudge-wrappers the deterministic owners of their command names so the devenv buildEnv no longer emits nondeterministic
collision between ...warnings (#808). Each guard now exec's the real binary by absolute store path underDT_PASSTHROUGH=1instead of grepping$PATH, so the real packages (genie,mr,oxlint,oxfmt,nixfmt,deadnix,tsgo,pnpm) no longer need to be competing top-level profile providers. Reals are threaded into the task modules via*Pkgargs (tsBinPkg,oxlintPkg,mrPkg,pnpmPkg,geniePkg).effect-tsgoandpnpmstay on$PATHatlib.lowPrioto keep theireffect-tsgo/pnpxsiblings reachable.realBinis optional with the previous$PATH-grep retained as the fallback, so thefromTasks/mkCliGuardAPI and unthreaded guards (vitest,playwright) are backward compatible. The exportedtasks.geniemodule stays a bare-path standard module —geniePkgis an optional module argument (threaded via_module.args.geniePkg), so downstreamimports = [ inputs.effect-utils.devenvModules.tasks.genie ]is unaffected. -
repo dependency/toolchain refresh: Upgrade the Nix, pnpm, TypeScript, lint, test, Storybook/Vite, React, OpenTelemetry, OpenTUI, and Effect 3-line dependency set to current major-compatible versions while deliberately not moving to Effect 4. Live installs now use Nix-managed pnpm 11 with global virtual store enabled, install scripts and dependency scripts disabled, strict store verification, and a pure workspace-local store; fixed-output Nix prep keeps an isolated virtual store as the reproducible exception because GVS projections point at the builder-local pnpm store. Native packages that are needed at runtime are supplied through Nix/custom package derivations, and Storybook oxlint rule exports are temporarily removed until the upstream ESLint 10 plugin stack is compatible again. Refresh the affected CLI
pnpm-depsfixed-output hashes so cold Nix prep validates after the lockfile and pnpm policy changes, explicitly denyfseventsbuild approval, and document the boundary between Nix-owned lifecycle-built native addons and locked fixed-output prebuilt optional native packages. ThegenieCLI now formats through the Nix-wrappedoxfmtexecutable instead of bundling the npmoxfmtmodule, keeping optional formatter plugins out of the compiled CLI closure. CI's shared Nix retry wrapper now repairs and retries a missing multi-user Nix daemon socket, matching the existing transient Nix store/fetch retry policy. Storybook is now declared as depending on this workspace's@storybook/react-viteframework package via pnpm package extensions, so Storybook's dynamic preset loading works under pnpm's global virtual store; refresh the lockfile and affectedpnpm-depsfixed-output hashes for that package-extension checksum change. The pty-effect native-package linking task now resolves the active pnpm global virtual store path and skips an absentlinksprojection instead of assuming the default workspace store exists while source-modemrtasks now wait forpnpm:installbefore resolving their package graph, removing a check:quick task-order race in CI perf probes and the Genie perf probe now uses the supportedgenie:checktask instead of raw Bun execution through shell entry (#804). -
devenv TypeScript tasks: Move the normal workspace TypeScript check/build path to the Nix-managed
tsgobinary, sots:check,ts:check:strict,ts:build,ts:build-watch, andts:cleanrun on the TypeScript 7 native compiler track.ts:emitkeeps using JavaScripttscfor its compiler-API tsconfig filtering and no-check emit path. The old standalonets-effect-lspcompatibility module is removed because Effect diagnostics are now part of the normal tsgo-backed check path. -
@overeng/genie: Make
findGenieFilesreturn stable repo-relative.genie.tspaths, with generation/check orchestration resolving them at the filesystem boundary. Discovery tests now assert through a canonical-relative helper so symlinked roots and macOS/varrealpath aliases cannot skew path expectations. -
repo: Add a no-op Hypermerge production canary for schickling/dotfiles#1072.
-
Notion docs: Ratify the durable Notion DB Markdown Sync VRS decisions into
context/notion-db-markdown-sync/decisions/0014through0026and remove the provisionaldecisions/proposedledger. -
repo-wide (oxlint enforcement): Made the oxlint gate strict now that every rule is at zero repo-wide. Promoted the swept rules
overeng/named-args,overeng/explicit-boolean-compare,overeng/exports-first,func-style, andno-await-in-loopfromwarntoerroringenie/oxlint-base.ts(overeng/jsdoc-require-exportswas alreadyerror), and flippeddenyWarnings = false→trueindevenv.nixso ANY warning now fails CI — enforced identically in CI and the locallint:checkpre-commit gate, so the gate can never silently regress. To reach a clean zero, the 7 residual non-overengadvisory warnings were resolved principledly:unicorn/prefer-array-find(a split-into-all-tokens false positive) andunicorn/consistent-function-scopingon the oteliteinspectArgshelper got justified inlineoxlint-disables; the puremillisToNanosconverter in restateRuntime.tswas hoisted to module scope;oxc/no-barrel-filewas scoped off forutils/node/otel.ts+otel-attrs.ts(intentional./node/otelentry / contract-convenience barrels, like the already-exemptmod.ts); andimport/no-cyclewas scoped off forrestate-effect/src/testing/**(a benign barrel-induced test-infra cycle, cf. the kdl port waiver). Result:oxlint … .reports 0 warnings / 0 errors anddt lint:check(now--deny-warnings) is green;genie --checkbyte-identical,dt ts:checkclean. (#771) -
repo-root
genie/tooling, @overeng/oxc-config, @overeng/megarepo, @overeng/notion-md: Drove the final batch ofoverengoxlint warnings (27: named-args + explicit-boolean-compare + exports-first) to zero across these targets — no lint-rule,.genie.tsgenerator,genie/oxlint-base.ts, or.oxlintrc.jsonchange, and no runtime/OTEL-bridge/generated-config-output change (genie --checkstays byte-identical).overeng/named-args: user-defined functions with 2+ positional parameters were converted to a single options object (parameter names + type parameters preserved verbatim) with all call sites updated in lockstep — the ten CI-workflow generator helpers ingenie/(appendGitHubEnvLine,annotationLine,ciJobConcurrency,withDefaultJobConcurrency,withGitHubAccessTokenEnv,withPrivateCachixReadAuth,nixCachePrimaryKey,vercelDeployStep, plusjob/downloadCurrentMeasurementArtifactStepin.github/workflows/ci.yml.genie.ts), the three internalno-raw-otel-primitives.tsAST helpers inoxc-config(trackEffectImport/rawOtelCallSource/rawOtelNamespaceCallSource— visitor callbacks themselves stay single-param), thetrustedWith/trustedAnnotatespan helpers inmegarepocli/libobservability and the file-localwalkrecursion inlib/store.ts, and thewithOperation/annotateAttrsspan helpers innotion-md(matching the existingnotion-effect-client{ operation, attributes }/{ attributes, value }convention) with their ~50 in-package call sites; the exportedwithLabelSpan/withRepoPathSpancross-module call sites ingit.ts/store*.ts/nix-lock/mod.tswere updated in lockstep. The genie source-asserting tests in@overeng/geniewere updated to the new call syntax (body-string and export-existence assertions left intact).overeng/explicit-boolean-compare: genuine strict booleans got explicit comparisons (strict === trueinstore-liveness.ts,dryRun === trueinstore/mod.ts,bodyIsEmpty === trueineditor-surface.ts). One waiver:editorTeardowninnotion-md/cli.tsimplements Effect's positionalTeardown(exit, onExit)interface required byNodeRuntime.runMain, so it carries an inlineoxlint-disable-next-line; thecli.tsexports-firstwarning was fixed by moving the exportedrunCliMainabove its non-exported helpers. The repo-widedt ts:check,oxfmt --check,genie --check(byte-identical), and the authoritative type-aware oxlint (alloverengrules at zero) stay green; the megarepo (606), notion-md (198), oxc-config (280), and genie (274) vitest suites pass. (#771) -
@overeng/otel-contract, @overeng/utils, @overeng/content-address: Drove the
overeng/named-argsoxlint warnings (12 + 8 + 3) to zero across the three core libs on the calling-convention axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no telemetry-contract / hashing / playwright-span semantics change. 23 user-defined functions with 2+ positional parameters were converted to a single options object (parameter names and type parameters preserved verbatim), with all in-package and cross-package call sites updated in lockstep. Inotel-contract/src/mod.ts: the widely-consumedOtelAttrattribute DSL (string/boolean/number/json, themetadatadefault preserved as an optional destructured property); the metric-emission API on theOtelEffect{Counter,Histogram,Gauge}interfaces AND their runtime bridges (incrementBy/trustedIncrementBy/record/trustedRecord/set/trustedSet, converted interface-signature + closure + return-object in lockstep); and the internaltaggedMetric/invalidMetricLabelhelpers. Incontent-address/src/mod.ts:canonicalJsonString/canonicalJsonBytes/hashCanonicalJson(value's type re-expressed asSchema.Schema.Type<TSchema>so it no longer depends on a positionalschemabinding). Inutils: the seven file-localtrustedWithspan helpers (browserBroadcastLogger, nodeActiveHandlesDebugger/cmd, playwrightop/page/wait/step) and the exportedwithFreePort. Because these are core exported surfaces, cross-package call sites were updated in@overeng/restate-effect(observability/contract.ts+observability/Metrics.ts),@overeng/megarepo(cli/observability.ts),@overeng/notion-effect-client(body-evidence.ts), and@overeng/notion-datasource-sync(core/canonical.ts). Zero waivers: nothing here implements an external interface (the rule's only waiver criterion).tscper package, the repo-widedt ts:check(the cross-package call-site safety net),oxfmt --check, and the authoritative type-aware oxlint (named-args at zero in all three packages, 0 errors) stay green; the content-address (4), utils (143), otel-contract (37), and restate-effect (170, against a native restate-server) vitest suites pass. (#771) -
@overeng/restate-effect, repo-root
genie/tooling: Drove theovereng/explicit-boolean-compareoxlint warnings (38) to zero on the readability axis — no lint-rule,.genie.tsgenerator,genie/oxlint-base.ts, or.oxlintrc.jsonchange, and no runtime/durability/journaling, OTEL-bridge (decision 0007), or generated-config-output change (genie --checkstays byte-identical: 75 generated files unchanged). Each operand was type-confirmed before fixing rather than mechanically appended with=== true: genuine strict booleans (compare/trustNeedsAdmissiondefaulted via?? true/= falseingenie/ci-workflow/measurements.ts+merge-queue.ts;retryable/stops/wakeOn/stopByCount(...)inScheduled.ts;Response.ok;Schema.Booleanfields likedecision.ok/approved/wedge/done; theserverAvailableprobe; boolean-returning predicates) became=== true/=== false. ThegatePolicy.enabledoperand (spread-widened toboolean | undefinedbut always set at runtime) took=== trueto preserve "absent ≡ not enabled", matching the file's existing=== trueusage. 31 sites in restate-effect (src, examples, integration tests) and 7 in the root genie CI-workflow tooling; zero justified disables.tsc,oxfmt, and the authoritative type-aware oxlint (explicit-boolean-compare at zero in both targets, no errors) stay green; the full restate-effect vitest suite passes (170 tests, 0 skipped — the integration lanes ran against a nativerestate-server). (#771) -
@overeng/restate-effect: Drove the
overeng/named-argsoxlint warnings (148) to zero on the calling-convention axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no runtime/durability/journaling or OTEL-bridge (decision 0007) semantics change. 141 user-defined functions with 2+ positional parameters were converted to a single options object (parameter names preserved verbatim) across src, examples, and tests, with their internal and cross-module call sites updated in lockstep; the builder oracle incapability-inference.types.tsconfirmed theconsttype-parameter inference survives destructuring. 7 sites were waived with inlineoxlint-disable-next-linebecause the positional signature is structurally fixed by an interface this code implements rather than calls: the materialized SDK handler(ctx, input)inEndpoint.ts, theRandom.RandommethodsnextRange/nextIntBetweeninRuntime.ts, and therestate.Context/ObjectContextmethodssleep/resolveAwakeable/rejectAwakeable/setinTestContext.ts. SDK/ingress positional calls the package merely invokes (e.g.client.workflowSubmit,ctx.resolveAwakeable) were left positional. Exported functions changed calling convention, so this is breaking for external callers (passing positionally must become a single options object); no in-repo cross-package call site required updating (there are none).tsc,oxfmt, and the authoritative type-aware oxlint (named-args at zero, no errors) stay green; the full vitest suite passes (170 tests, 0 skipped — the integration lanes ran against a nativerestate-server). (#771) -
@overeng/notion-effect-client, @overeng/notion-core, @overeng/pty-effect, @overeng/tui-react, @overeng/notion-datasource-sync, @overeng/notion-cli, @overeng/genie: Drove the
overeng/named-argsoxlint warnings to zero across these seven leaf packages on the calling-convention axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no runtime/OTEL-bridge (decision 0007) semantics change. 17 user-defined functions with 2+ positional parameters were converted to a single options object (parameter names and type parameters preserved verbatim), with all in-package internal and cross-module call sites updated in lockstep: thewithOperation/annotateAttrs/pushProcessorData/treeEntrieshelpers innotion-effect-client;daysInMonth/compareNotionApiVersionsinnotion-core/constants.ts; thetrustedWith/trustedAnnotate/withSchemaVersionOTEL + projection helpers ingenie;trustedWith/withPtyNameSpan/withPtyOperationSpan/withPtyWaitSpaninpty-effect; and the exportedbodyIdentityEqualsinnotion-datasource-sync(call sites inbody/adapter.ts+body/notion-md.ts). The twonotion-coreincludesLiteraltype-guards were curried rather than object-destructured because avalue is TValuetype predicate cannot bind to a destructured property. Two functions were waived with inlineoxlint-disable-next-linebecause their positional(exit, onExit)signature is structurally fixed by Effect'sTeardowninterface they implement and pass toNodeRuntime.runMain:tuiTeardown(tui-react) andeditorTeardown(notion-cli). No exported function in these packages has an out-of-package (core-lib/contended) call site, so no cross-package signature change was needed.@overeng/oxc-config's 3 remaining named-args are all internal helpers inside theno-raw-otel-primitives.tsrule file, which is out of scope here.tsc(per package),oxfmt, and the authoritative type-aware oxlint (named-args at zero in these seven packages, no errors) stay green; each package's vitest suite passes (notion-datasource-sync has env-gated integration skips). (#771) -
@overeng/utils-dev, @overeng/utils, @overeng/notion-core, @overeng/notion-effect-client, @overeng/genie, @overeng/otel-contract: Drove three file-local oxlint rules to zero across these packages on the readability axis — no lint-rule, genie-source, or
.oxlintrc.jsonchange, and no function-signature, call-site, or runtime-semantics change.overeng/explicit-boolean-compare(9): replaced implicit boolean coercion with explicit comparisons matching each operand's actual type — genuine-boolean!fn(...)/if (x)becamefn(...) === false/=== true(thematchesMetricValue/matchesAttrs/matchesSelectorselector helpers inutils-dev,settled/collidedinutils/node/net.ts,droppedNotRoundTripSafeinnotion-core/body-fidelity.ts); thenotion-effect-clientrate-limit ternary kept itsOption.isSomealiased-narrowing through the=== trueform (verifiedrateLimit.valuestill type-narrows).eslint/func-style(9): convertedfunctiondeclarations to arrow-function consts where no hoisting/this/generator constraint applied — the sixprojection-artifact/mod.tsruntime helpers ingenie(all references deferred inside closures, so the const reorder is safe) and the threewithOperation/withRootOperation/withOperationStreamnested builders inotel-contract/src/mod.ts(defined before the return object that exposes them).overeng/exports-first: no in-scope occurrence. Zero justified disables.tsc(per package),oxfmt, and the authoritative type-aware oxlint stay green; the touched packages' vitest suites pass except for pre-existing otelite-binary /WORKSPACE_ROOT-env-gated tests in files not touched here. (#771) -
@overeng/notion-effect-client: Stop enabling GFM autolink literals in canonical Markdown. The canonicalizer now composes only the GFM features it needs (tables, task-list items, strikethrough), so bare URLs and email-shaped text remain plain text instead of being rewritten to angle autolinks; this fixes the
FAST_CHECK_SEED=199297095idempotence failure (0@.A-><0@.A>-><<0@.A>>) and avoids lossy Notion preview-link text canonicalization. Refresh the affected pnpm lock/package-closure hashes. (#771) -
@overeng/utils-dev: Add a reusable Vitest fast-check replay setup that reads
FAST_CHECK_SEEDandFAST_CHECK_PATH, so CI-reported property-test failures can be replayed locally without editing the failing test. Adopt it in@overeng/notion-mdunit tests and refresh the affectednotion-clipnpm-deps FOD hash after the generated package export change. (#771) -
@overeng/notion-effect-client: Drove the
overeng/jsdoc-require-exportsoxlint warnings (16) to zero on the docs/visibility axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no client/OTEL/body-evidence-semantics change. Reachability from the published entry (src/mod.ts, transitively following its re-export chains) plus cross-module/test imports was the discriminator across three files. The fourinternal/otel.tsspan helpers (withNotionHttpSpan/annotateNotionHttpRateLimitSpan/withNotionDatabasesQuerySpan/withNotionPagesRetrieveSpan) are imported by sibling modules (internal/http.ts,databases.ts,pages.ts) so they kept theirexportand gained span-name/label-rule JSDoc. Inbody-observation.tsthemod.ts-reachableNotionBodyObservation/observeFromSnapshots/NotionBodygained concise invariant-carrying JSDoc, while the standaloneobserve(in no entry, imported nowhere by name, reached only via theNotionBodyobject) was un-exported. Inbody-evidence.tsthe fivemod.ts-reachable symbols (BodyEvidenceFingerprint/RemoteBodyObservationEvidence/fingerprintBodyEvidence/makeRemoteBodyObservationEvidence/descriptorDigest) gained JSDoc (e.g. the fingerprint excludes the volatileobservedAtso re-observing unchanged content is stable); the module-internalBodyCompletenessEvidenceconst+type and theBodyEvidenceBlockTreetype were un-exported; and the deadbodyEvidenceDescriptorDefaultsconst — unreachable AND referenced nowhere — was deleted (with its now-unusedcanonicalJsonMediaType/canonicalJsonCodecimports). Buckets: 3 un-export, 10 real-doc, 1 delete. No symbol left the published API surface;tsc/oxfmt/the full vitest suite (137 tests; one otelite-CLI-spawning span-shape test skipped as it requires the out-of-PATHotelitebinary) stay green. (#771) -
@overeng/content-address: Drove the
overeng/jsdoc-require-exportsoxlint warnings (19) to zero on the docs/visibility axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no hashing/content-address-semantics change. The package is a single-file library whose published entry ISsrc/mod.ts, so every warned symbol is directly exported from the entry AND imported by downstream packages — all reachable. Each therefore stayed exported and gained a concise, non-trivial one-line JSDoc carrying a real invariant or gotcha rather than a name restatement (e.g.ContentDigestis lowercase-hexsha256:<64 hex>;canonicalJsonString/hashCanonicalJsonsort object keys so the digest is stable across key-insertion order;canonicalJsonBytesis the exact bytes hashed;verifyDescriptorfails closed viaContentDescriptorMismatchErroron digest or byte-length disagreement;objectPathForDigestsplits on the first hex byte to avoid huge flat dirs;descriptorForCanonicalJsonrequires an explicitschemaVersion). Zero un-exports, zero deletions.tsc/oxfmt/the full vitest suite (4 tests) stay green. (#771) -
@overeng/genie: Drove the
overeng/jsdoc-require-exportsoxlint warnings (28) to zero on the docs/visibility axis — no lint-rule, genie-config-source, or.oxlintrc.jsonchange, and no runtime/generation/OTEL-semantics change. Reachability from the published entries (.→runtime/mod.ts,./cli,./sdk) was the discriminator. The sevenprojection-artifact/mod.tsexports are all re-exported viaexport *fromruntime/mod.ts, so they stayed exported and gained concise, non-trivial one-line JSDoc (e.g.ProjectionJsonObjectrejects top-level arrays/primitives;projectionArtifact.jsonemits stable schema-versioned JSON while keeping typed.data;defineProjectionValidatoris an inference-pinning identity helper).core/observability.tsis module-internal (in no entry) and consumed only viaimport * as Observability, so a symbol is reachable iff accessed asObservability.<name>(or the one namedwithCliModeSpanimport inbuild/mod.tsx); the thirteen reachablewith*Span/annotate*/relativePathhelpers kept theirexportand gained span-name/label-rule JSDoc, while eight module-internal*Span/*Attrsoperation/schema consts that build their paired wrapper in the same file but are never accessed externally were un-exported (commandSpan,fileSpan,pathAttrs,oxfmtAttrs,validationSpan,atomicWriteSpan,importMapResolverSpan,targetLockAttrs). No symbol left the published API surface; zero deletions;tsc/oxfmt/the full vitest suite (268 tests) stay green. (#771) -
@overeng/notion-core: Drove the
overeng/jsdoc-require-exportsoxlint warnings (25) to zero on the docs/visibility axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no runtime/Notion-API-schema-semantics change. Reachability from the single published entry (mod.ts) was the discriminator: every warned symbol acrossids.ts,properties.ts,colors.ts,constants.ts, andbody-fidelity.tsis re-exported frommod.ts, so all stayed exported and gained a concise, non-trivial one-line JSDoc carrying a real invariant or gotcha rather than a name restatement (e.g.NotionUuidis canonical dashed form;SELECT_COLORSis the solid-only subset with no*_backgroundvariants;NOTICON_COLORSaddslightgrayand omits backgrounds;BodyLossyReason/BodyCompletenesscarry the R30/R38 round-trip verdict;stableBodyFidelityStringifysorts object keys for stable hashing). Zero un-exports, zero deletions.tsc/oxfmt/the full vitest suite stay green. (#771) -
@overeng/notion-md: Drove the
overeng/jsdoc-require-exportsoxlint warnings to zero on the docs/visibility axis, using reachability from the published surface (mod.ts/./cli/./cli-program) plus cross-module/test imports as the discriminator — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no runtime/rendering/reconciliation/API-shape change.observability.tsis module-internal (not in any entry) and consumed only asimport * as Observability, so a symbol is reachable iff accessed asObservability.<name>. Thirteen*Attrsschema consts that build their pairedOperation/span in the same file but are never accessed externally were un-exported (pageAttrs,parentPageAttrs,dataSourceAttrs,pathAttrs,pathRecursiveAttrs,pathPlanAttrs,pathSyncAttrs,pagePathAttrs,pushSpanAttrs,stateFileAttrs,objectRoleAttrs,markdownUpdateAttrs,metadataUpdateAttrs). Four exported encoder helpers (page/parentPage/path/pagePath) were dead — unreachable AND never called internally — so un-exporting them would tripno-unused-vars; deleted instead (with the now-unusednode:pathbasenameimport). The reachable surface gained concise one-line JSDoc carrying span name / label rule / invariant: the namespace-reachable spans (StatusPathSpan…GatewayArchivePageSpan,stateFileSpan),withOperation/annotateAttrs, the externally-referenced*Attrs(statusAttrs/pushResultAttrs/syncResultAttrs/objectHashAttrs/pushDecisionAttrs/pushMarkdownCommandAttrs/pushDecisionMarkdownCommandAttrs), the fivebody-facade.tsbody-snapshot interfaces re-exported frommod.ts,remoteMarkdownFromBodyObservation(live.ts, test-reachable), andbuildFrontmatterV2(sync.ts, inmod.ts). No symbol left the published API surface;tsc/oxfmt/the full vitest suite (198 tests) stay green. (#771) -
@overeng/notion-datasource-sync: Drove the
overeng/jsdoc-require-exportsoxlint warnings (17) to zero on the docs/visibility axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no sync-semantics/behavior change. Reachability from the published entries (.→src/mod.ts'sexport *splat ofcore/domain.ts; the./body/notion-md,./local, and re-exported./local/sidecar.tssurfaces) plus cross-module imports was the discriminator. Every warned symbol was reachable, so all kept theirexportand gained a concise, non-trivial one-line JSDoc carrying a real invariant or gotcha: the tencore/domain.tsbody-identity helpers (hashFromContentDigest/bodyEvidenceFingerprintFromContentDigestreinterpret an existing content-address digest without rehashing;bodyIdentityDigestis evidence-fingerprint-when-present else rendered-digest;renderedBodyDigestignores evidence;bodyDescriptorForDigestcarriesbyteLength: 0since the bytes are absent;evidenceBackedBodyIdentitykeeps lossy renders distinguishable), the./localFilesystemWorkspaceSidecarschema/type re-export, the three cross-modulelocal/sidecar.tsexports (metadataDirectoryNameis the hidden, scan-skipped metadata dir;pageSidecarDirectoryName;makeFilesystemWorkspaceSidecarderives the own-write suppression token), and the twobody/notion-md.tsstructural*Likeadapter types that decouple this package from the exact upstream@overeng/notion-mdbody shape. Zero un-exports, zero deletions.tsc/oxfmt/the full vitest suite (441 tests) stay green. (#771) -
@overeng/utils: Drove the
overeng/jsdoc-require-exportsoxlint warnings (4) to zero insrc/node/otel.tson the docs/visibility axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no telemetry-foundation (withTelemetry/Shape/makeOtelCliLayer/sampleResource) behavior or shape change. All four warned symbols are reachable from the published./node/otelentry (makeOtelCliLayeris also imported by downstream CLIs; the three option interfaces are the parameter types of the exportedwithTelemetry/sampleResource/sampleGaugefunctions, so they stay in the.d.tssurface), so none were un-exported or deleted.makeOtelCliLayer's warning was a misattached doc: its rich block comment had been orphaned when thedefaultShutdownTimeoutMshelper (with its own JSDoc) was inserted between the doc and the function — moving the helper above the doc block re-attaches the existing documentation rather than adding a new comment. The three options interfaces (TelemetryLayerOptions,SampleResourceOptions,SampleGaugeOptions) gained a concise one-line JSDoc naming their consuming function and the inputs they carry (their members were already documented). Buckets: 0 un-export, 4 real-doc (1 re-attach + 3 new), 0 delete. No symbol left the published API surface;tsc/oxfmtstay green and the non-otelite vitest suite passes (137 tests; 6 otelite/WORKSPACE_ROOT-env-gated tests skipped, as they require the out-of-PATHotelitebinary + devenv env that is broken on this branch). (#771) -
@overeng/megarepo: Add the default-enabled, clean-only
mr store gcarchival path forref_mismatchworktrees from decision 0008. A mismatch may be archived only when both refs pass default/live guards, the worktree is clean, local work is remote-reachable, no stash exists, and absence grace has elapsed. Archives record both the store path ref and actual HEAD branch, detach the archive, and preserve branch refs instead of guessing which name to delete; JSON output exposespathRef/actualHeadBranch, and GC span tallies now include archived/reaped/kept counts for fleet verification. -
@overeng/megarepo: Drove the
overeng/jsdoc-require-exportsoxlint warnings inlib/observability.ts+cli/observability.tsto zero by treating reachability from the package's published/imported surface as the fix axis. The two observability modules are consumed only as namespace imports (import * as Observability/LibObservability), and neither is re-exported frommod.tsor the CLI entry — so a symbol is reachable iff it is accessed asObservability.<name>by some consumer (verified per-symbol, disambiguating the overloadedObservabilityalias, which resolves tocli/observability.tsfromcli/commands/*but tolib/observability.tsfromlib/*). The reachablewith*Span/annotate*span helpers (plusshortPath) kept theirexportand gained a concise one-line JSDoc stating the span name / label rule / invariant. The unreachable module-internal*Attrsschema consts (used only to build their pairedOperationin the same file) were un-exported (dropexport). Seven exported encoder helpers (label/repoPath/worktreePath/workspaceRoot/storeWorktree/storeSource/pathLabel) were dead — unreachable AND never called internally — so merely un-exporting them would tripno-unused-vars; they were deleted instead (with the now-unusednode:pathimport). No lint-rule, genie-source, or.oxlintrc.jsonchange;tsc/oxfmt/the full vitest suite are unaffected. (#771) -
@overeng/restate-effect: Drove the
overeng/jsdoc-require-exportsoxlint warnings (26) to zero on the docs/visibility axis, treating reachability from the package's four published entries (mod.ts/./otel/./admin/./testing) and cross-module/test imports as the discriminator — no lint-rule, genie-source,.oxlintrc.json, or runtime/OTEL-semantics change (decision 0007 untouched). Three module-internal observability schema/array consts that were unreachable AND used only to build their paired definition in the same file were un-exported (trustOtelContract,RestateOperationAttributes,RestateDurationMetricBoundariesMs). The reachable surface gained concise, non-trivial one-line JSDoc carrying purpose/invariant: the six baselineMetricre-exports, theBoundary*Attrs/RestateMetrics/restateOperation/withRestateOperationobservability seams, theScheduledcompanion const/type pairs (Schedule/OnCycleError/WakePayload/StatusOutput/ScheduledConfig/RestateScheduled),ContractSerdeFactory, and theRestatenamespace. Three already-rich block comments that the JSDoc rule missed were upgraded/*→/**(RestateIngress,RestateTestHarness,RestateTestEnvTags), and theendpoint/layereslint-disableline moved above its JSDoc so the doc re-attaches. No symbol left the published API surface;tsc/oxfmt/the full vitest suite (170 tests) stay green. (#771) -
@overeng/utils-dev: Drove the
overeng/jsdoc-require-exportsoxlint warnings (41) to zero across the otelite test-harness modules (otelite/otel.ts,trace-expect.ts,signal-expect.ts,test-harness.ts) on the docs-only axis — no lint-rule, genie-source,.oxlintrc.json, or harness-behavior change (the test↔prod SIBLING convergence stays intact). Reachability was the discriminator: as the universal test-harness leaf, almost every export is public via the./oteliteentry (which transitively re-exports the matcher DSL, selectors, errors,OteliteTestHarness, and thecapture*helpers) or, for the fivewithOtelite*Spanspan helpers inotel.ts, reachable as cross-module imports fromOtelite.ts/test-harness.ts. No symbol was unreachable, so all 41 kept theirexportand gained a concise, non-trivial one-line JSDoc carrying a real invariant or gotcha (e.g. matchers stringify primitives because otelite rows are flat strings;nullmatches the literal'null';spanLabelPolicydefaults torequired;withOteliteRootSpanforces a fresh trace) rather than a name restatement. Zero un-exports, zero deletions.tsc(utils-dev + the@overeng/utilsconsumer),oxfmt, and the full vitest suite (30 tests) stay green. (#771) -
@overeng/otel-contract: Add a public
<project>-<role>service-name / resource-identity shape helper.ServiceNameFromParts(aSchema.transformOrFail(ServiceNameParts, OtelServiceName)) buildsservice.name =${project}-${role}``from typed parts, validated in two load-bearing layers:project/`role`decode as plain`NonEmptyTrimmedString`(rejecting empty/whitespace) BEFORE the joined string decodes through the existing`OtelServiceName`brand — because`OtelServiceName`'s pattern admits a trailing hyphen, an empty `role`would otherwise compose to`"-"`and silently pass a single decode.`FleetServiceBinding` is the public type-seam: a plain-`string` `{ project, role, namespace, version }`interface describing the SHAPE a private fleet config supplies (this repo owns the TYPE + constructor; a private repo supplies the VALUES — zero fleet values here, fields unbranded so decode stays at the edge).`serviceIdentityFromBinding(binding)`assembles a validated`ServiceIdentity`from a binding, removing the hand-rolled`Schema.decode(ServiceIdentity)({ name: `${project}-${role}`, … })`at composition roots. Parts are plain (not branded) — the lightest typing that still rejects empties, since the composed`OtelServiceName`decode already enforces the naming law (decision 0003 in`@overeng/utils/docs/vrs/.decisions/`). (#771) -
@overeng/otel-contract: Drove the
overeng/jsdoc-require-exportsoxlint warnings (10) to zero on the docs/visibility axis — no lint-rule, genie-source, or.oxlintrc.jsonchange, and no schema/brand/cardinality/no-raw-otel-primitives-boundary change. The package is a single-file telemetry contract whose published entry ISsrc/mod.ts, so every warned symbol is directly exported from the entry (and depended on by every downstream package) — all reachable. Each therefore stayed exported and gained a concise, non-trivial one-line JSDoc carrying the brand law or instrument invariant rather than a name restatement: the four name brands encode their distinct pattern rules (OtelAttributeKey/OtelServiceNameare letter-led[A-Za-z0-9_.:-];OtelSpanNameadmits any printable ASCII incl. spaces/punctuation;OtelMetricNameis Prometheus-style and may lead with_/:),OtelMetricInstrumentKindselects which definition/runtime bridge applies,OtelHistogramDefinition/OtelGaugeDefinitionnarrow the metric definition (histogram adds bucketboundaries; gauge is last-set), andOtelEffect{Counter,Histogram,Gauge}Metricalias the underlying Effect runtime each contract drives. Zero un-exports, zero deletions.tsc/oxfmt/the full vitest suite stay green. (#771) -
@overeng/megarepo: Reframed the store-root
_-prefix membership guard (shouldSkipStoreRootEntryinlib/store.ts) as the PRIMARY, intentional boundary between member namespaces (<host>/<owner>/<repo>/) and non-member co-tenant dirs that legitimately share the store root — not a temporary band-aid awaiting a writer-side fix. The_iso/<slug>/entries that the store walk skips are continuously written by external isolation tooling (PR-verification isolated worktree checkouts; each a worktree client with a.gitFILE pointing at a bare, plus a full working tree), i.e. a production runtime co-tenant, not a test/bench artifact — so the read-side skip is the correct, durable contract and is KEPT, with its comment corrected to say so. ThelistReposlayout regression test now also exercises the real_iso/<slug>worktree-client shape (.gitfile + working tree), proving the_-prefix root skip excludes it (and never descends its working tree) BEFORE the.gitshape is ever inspected. The co-tenant writer lives outside this package (in the isolation tooling that owns the_iso/namespace); the store-side membership boundary is the in-scope, principled fix. (#771) -
@overeng/notion-md: Compacted the VRS decision log by removing six superseded
.decisions/records and redirecting every citation to the surviving decision. The reconciler/placeholder design —0005inline id-carrying placeholders,0010visible-placeholder deletion,0011block-level reconciliation,0014reconciliation-as-universal-push-engine,0015renderer-symmetric Markdown↔block converter — is wholly superseded by decision 0016 (refuse lossy pages), and the0013stateless in-buffer schema fingerprint by decision 0017 (ephemeral file-engine session, drift detected from the engine's base snapshot). The six records were deleted rather than tombstoned (their rationale lives inexperiments.md), and all references — theschema-snapshot.tsdoc-comment, the01-editor/04-fidelity/06-data-sourcespec/requirements,experiments.md,README.md, and the surviving decision records0003/0008/0009/0016/0017/0019— were redirected to the superseding decision (as a live link where one is cited, or as historical prose naming the superseded concept).0016/0017were tightened to state the supersession once, and the duplicated "Refined by 0017" blockquotes on0003/0008/0009trimmed to one line. No decision IDs were renumbered; the six deleted IDs simply disappear (decision log is now 13 records:0001–0004,0006–0009,0012,0016–0019). Zero links resolve to a deleted record. Docs-only — no library surface change. -
@overeng/notion-datasource-sync: Body is SINGLE-SURFACE and ADAPTER-OWNED with a resolution path (#775, decision 0021). Revises the earlier "unify body convergence through the engine" milestone (M2b): routing the
.nmdBODY surface through theconvergeLocalSurfacesengine was a FALSE SYMMETRY —.nmdis the only local body surface, so the engine always saw a single body fact and added no handling the body adapter does not already do (dead-weight ceremony plus a latentbody-body-delegatedstuck-state). PART A removes the inert engine routing: thenmdBodyFactbuilder + itsconvergeLocalSurfacescall inpushOneShotSync's body loop (src/sync/sync.ts), the engine-detectedbody-body-delegatedconflictKindfromConflictRaised(src/core/events.ts) andConflictProjectionRow.kind(src/store/store.ts), and theraiseConvergenceConflictsbody-raise extension insrc/cli/main.ts(reverted to a property-only inline loop). The convergence engine is now PROPERTY-only; the body adapter is the sole body authority (conflictKind: 'body', raised fromplanLocalChange→BodyConflict). The genericclassifyConflictbody-vs-body classifier and the property/lifecycle conflict machinery are UNTOUCHED. PART B adds the body conflict resolution path (the real, reachable gap — the adapterbodyconflict was previously refused byconflict-commands.tsas a null-propertyIdnon-lifecycle conflict). A newresolveBodyConflictbranch (src/planner/conflict-commands.ts, mirroringresolveLifecycleConflict):keep-localre-asserts the local.nmdbody by re-enqueueing aBodyPushCommandagainst the current body pointer (routed through the planner so the body-edit guards apply; settles via the body settle handler);keep-remoteaccepts the remote body — theConflictResolvedstore apply gains abodyarm that retires the conflict and records the re-materialization intent by clearingsidecar_identity_provenon the body pointer. That intent is CONSUMED on the next pull:pullOneShotSynccollects every body pointer whosesidecarIdentityProvenis false and passes them asforceMaterializePageIdstoobserveRemoteDataSource, which overrides the mirror path's globalmaterializeBodyArtifacts: falsesuppression for exactly those pages, so the still-diverged local.nmdIS rewritten from the remote observation (a deferred remote effect, exactly as the lifecycle keep-remote arm defers its reconvergence) rather than the divergence persisting silently. Body has no engine-mergeable value (it is content), so resolution is re-push / re-materialize, NOT a value merge. Covered by e2e tests: a reachable adapterbodyconflict is raised AND resolvable (keep-local re-enqueues aBodyPushCommand; keep-remote resets sidecar identity, clears, and a follow-up suppressed-mirror pull STILL re-materializes the page — proving the intent is consumed) with replay determinism, plus the property convergence + lifecycle conflict + broader sync e2e suites stay green (regression). Ratified as decision 0021. -
@overeng/notion-datasource-sync: Make lifecycle (trash-state) conflict detection BIDIRECTIONAL, completing decision 0026 for the trash surface (#775, XC-R02/REPLICA-R12). The prior implementation caught only the
RowObserveddirection (remote RESTORE after a settled local ARCHIVE). This adds the symmetric TOMBSTONE direction: a remote TRASH after a settled local RESTORE. A remote trash does not arrive as aRowObserved(a trashed page drops out ofdata_source.query) — it arrives asTombstoneRecorded(reason='remote_trash'), which previously silently set_nds_row.in_trash = 1, overriding a settled local restore. A newtombstoneLifecycleDivergenceEventsdetector (src/sync/sync.ts) runs at the disappearance/absence append seam inpullOneShotSync: for aTombstoneRecorded(remote_trash)(R = 1) it computes the SETTLED local targetLviareadSettledLifecycleTargetand, whenL = 0(settled RESTORE) diverges fromR, appends aConflictRaised(conflictKind: 'lifecycle', remoteInTrash: true)plus thePendingIntentShadowViolationdiagnostic at a LOWER sequence than the tombstone — exactly mirroring theRowObservedseam.L = undefined(no settled intent) orL = 1(matchesR) stays benign (the false-positive guard). The tombstone'sin_trash = 1apply (src/store/store.ts) is now gated on the SAME#hasOpenLifecycleConflictcheck theRowObservedapply uses, so a settled local restore (in_trash = 0) is not silently flipped while the conflict is open/resolving. Resolution is unchanged and direction-agnostic:keep-remoteadopts the recordedremoteInTrash(= 1, tombstone stands),keep-localre-assertsL = 0via a re-enqueuedRestorePage(theresolvingstate holds the freeze until it settles). Replay-pure (theConflictRaisedprecedes the tombstone; noLrecomputation in the apply path). Ratified as decision 0026; its SCOPE note now states the conflict is bidirectional and the "symmetric direction out of scope / deferred to periodic reconcile" wording is removed. -
@overeng/notion-datasource-sync: Final-pass traceability + ADR reconciliation for the lifecycle and body convergence rails (#775). Register two concrete scenarios in
src/testing/scenarios.ts:NDS-L3-lifecycle-divergence-conflict(requirementIds: ['R82'], guardPendingIntentShadowViolation,src/e2e/sqlite-storage-contract.e2e.test.ts) for the decision-0026 remote-restore-after-settled-archive conflict, andNDS-L3-body-convergence-rail(guards: [],src/e2e/local-convergence-production.e2e.test.ts) for the FORWARD-LOOKING, production-inertbody-body-delegatedrail (a constructed second body surface drives the engine body-divergence fact + conflict-rail projection (page-keyed, null property_id) + per-surface blocking — NOT a production body conflict; the complementary no-in_trash-freeze isolation is covered separately insrc/store/store.unit.test.ts). ReconcilePendingIntentShadowViolation's genuine coverage: it was claimed onNDS-L3-realistic-local-remote-conflict(andNDS-L4-bidi-clean-outbound-after-remote-observation), but those tests never reference the guard — it is wired insync.tsand asserted only in the lifecycle-conflict tests, soguardScenarioIdsis repointed to the new lifecycle scenario and the overclaimedguards:entries are stripped (requirement coverage on those scenarios is unchanged). TheR82unmapped-requirementresidual is removed now that the lifecycle scenario maps it. ADR0021-body-is-single-surface-and-adapter-ownedgains a Consequences note clarifying that (a) the rendered-digest base applies to the ENGINE's local-divergence fact ONLY — the conflict/intent/command bases correctly stay on the evidence digest becauseguardStaleSurfaceBasecompares against the evidence-space projection — and (b) abody-body-delegatedconflict is currently UNRESOLVABLE through the user-action surface (conflict-commands.tsrefuses null-propertyIdnon-lifecycleconflicts) while still counting as not-clean / blocking compaction, a prerequisite to address before a second body surface is wired. No production code changed. -
@overeng/notion-datasource-sync: Extend the
ExpiringFileUrlfile-durability guard to catch obviously-expiring EXTERNAL URLs, not just Notion-hosted files (#775, decision 0024 upgrade — part 3). Durability is a property of the URL, not its source: previously anyexternalUrlwas treated as durable, so an S3 presigned link, an Azure SAS, or a GCS signed URL was attached as "durable" even though it expires just like a Notion-hosted signed link. A new pureisExpiringExternalUrl(url)helper (src/planner/property-proof.ts) detects OBVIOUS presign/expiry signatures in the URL query string (case-insensitively): AWS S3 (X-Amz-Signature/X-Amz-Expires/X-Amz-Credential), Azure SAS (sigwithse/st), GCS signed (X-Goog-Signature/X-Goog-Expires), and generic shapes (Signature+Expires, a unix-tsExpires,token+exp).evaluateDesiredFileReferencesnow classifies a flagged external URL as non-durable and routes it through the SAME existingguardExpiringFileUrl(mapping it to thenotion-hosted/no-stableRefsnapshot the guard blocks — reuse, not a re-implemented block), so it surfaces asBlockedByGuard/ExpiringFileUrlat planning. A durable external URL (no expiring signature, including benign query params like?utm_source=/?v=2/a bare?token=API key) stays allowed; a Notion-hosted file (noexternalUrl) stays blocked. Detection is deliberately CONSERVATIVE to avoid false-positives on durable URLs that merely carry benign params; a malformed/non-parseable URL carries no detectable signature and is treated as durable (fail-OPEN in the SIGNATURE detector —isExpiringExternalUrlreturnsfalse, never throws — while the no-externalUrlpath still fails closed independently). RESIDUAL: non-obvious non-durability (a plain durable-looking URL that 404s tomorrow) stays undetectable. TheNDS-L1-expiring-file-url-property-writescenario coverage note is broadened to the URL-durability framing. Ratified as decision 0024. -
@overeng/notion-datasource-sync: Wire the
ExpiringFileUrlguard onto propertyfileswrites (#775 M3b, decision 0024 part 2). The guard had been defined and unit-tested before this production dispatch. A new pureevaluateDesiredFileReferences(src/planner/property-proof.ts) inspects the desiredfilesproperty value at the property-write planning seam and FAILS CLOSED when anyCanonicalFileValuelacks anexternalUrl: aCanonicalFileValueis durable identity only with anexternalUrl(projected from the remotefile.external.url), whereas a Notion-HOSTED file is canonicalized toname+identityHashwith its signed/expiringfile.file.urlintentionally dropped — so a missingexternalUrlIS the "notion-hosted, no stable durable ref" case the guard names. Per file it builds aFileReferenceSnapshotand delegates the verdict to the existingguardExpiringFileUrl(reuse, not a re-implemented block). The planner pushes this decision intobaseGuardsright afterevaluatePropertyWrite(src/planner/planner.ts), so a notion-hosted files write that the property-write core ALLOWS (tag-fit is a no-op — afilesvalue fits afilescolumn) is surfaced asBlockedByGuard/ExpiringFileUrlbyfirstBlocked; an external durable URL proceeds, and an emptyfiles: [](a clear) and every non-fileswrite allow. REACHABILITY: this is the external-URL-onlyfilesseam — the encode path already fails closed generically (unsupported_remote_shape) when any file lacksexternalUrl; this change makes that same case fail EARLIER and SPECIFICALLY withExpiringFileUrlat planning. It is reachable via a pulled notion-hosted file round-tripped back as a write (pull → canonicalize to name+hash with noexternalUrl→ the signed URL is gone → writing it back as durable identity is exactly the unsafe case).CanonicalFileValuehas nofile_upload_idfield, so a durable pre-uploadednotion_filecannot be represented at this seam — every non-external file blocks, the correct fail-closed posture. TheExpiringFileUrlplaceholder-guard-scenarioresidual is promoted to a concreteNDS-L1-expiring-file-url-property-writescenario (requirementIds: ['R20', 'R52', 'R59'], guardExpiringFileUrl) insrc/testing/scenarios.tsand the placeholder removed, so the traceability self-test shows real coverage. Ratified as decision 0024. -
@overeng/notion-datasource-sync: Unify body convergence through the engine (#775 M2b, decision 0021). The
.nmdBODY surface now routes through the sameconvergeLocalSurfacesengine as properties for LOCAL divergence only, with per-surface blocking and a RENDERED-digest base — while the body ADAPTER stays authoritative for everything remote. A newnmdBodyFactbuilder (src/sync/local-convergence-inputs.ts) emits a bodyNmdDesiredFactwithdesiredHash = observation.contentHash(already rendered space) andbaseHash = renderedBodyDigest(snapshot body pointer), gated on actual divergence so an unedited pulled.nmd(evidence digest differs, rendered identical) does NOT false-diverge. The engine pass runs inpushOneShotSync's body loop (observation locality —observation.contentHashis a sidecar-entangled raw-file hash the CLI cannot reproduce), purely additive:.nmdis the only local body surface today (the SQLitebody_patchchannel stays dormant), so the engine always sees ONE body fact →single-surface→ no conflict, no block in production, and the bespoke edit-detection + the adapter's full remote-staleness/safety gate (lossy / unknown-blocks / would-delete-children / synced-page / non-body-mutation) andBodyPushCommandconstruction still run on every surviving body intent (risk-#1 adapter-authoritative invariant, regression-guarded). The conflict rail is EXTENDED, not collapsed: a newbody-body-delegatedconflictKind(engine-detected local body divergence, distinct from the adapter's own remotebodyconflict) is added toConflictRaisedandConflictProjectionRow.kind, page-keyed with a NULLproperty_id(no replica-schema change), raised through a now-extractedraiseConvergenceConflictshelper and projected to_nds_replica_conflicts.#hasOpenLifecycleConflictstayslifecycle-ONLY so abody-body-delegatedconflict does NOT freezein_trash. SCOPE: this milestone has NO production body-behavior change — the engine body pass is single-surface/inert, the builder base is inert in production, and the body intent/_nds_body_pointerprojection stay on the EVIDENCE digest (the planner'sguardStaleSurfaceBasecompares the intent base against the evidence-basedcurrentHash, so both must share that space; the rendered-digest base belongs ONLY to the engine's local-divergence builder). Everything added activates with the (forward-looking) second body surface. Ratified as decision 0021. -
@overeng/notion-datasource-sync: Treat page lifecycle (trash-state) divergence as a first-class conflict (#775 M2a'-2, decision 0026 parts 2 & 3, XC-R02/REPLICA-R12). A
RowObservedwhose remote trash stateRdiverges from the SETTLED local lifecycle targetLis now CONFLICT-DETECTED instead of silently flippingpages._in_trash(silent last-writer-wins on a bidirectional surface). The motivating case: a local archive settles (remote trashed), then someone independently RESTORES the page in Notion; the next sync'sRowObservedwould have silently reset_in_trashto 0, overriding the user's archive intent.Lis read from the SETTLEDTrashPage/RestorePageoutbox history (newNotionSyncStore.readSettledLifecycleTarget), NOT from the overloaded_nds_row.in_trash— readingin_trashforLwould manufacture false-positive conflicts on benign remote changes. Detection runs at the sharedpullOneShotSyncingestion seam (one-shot AND watch): on divergence it appends aConflictRaised(conflictKind: 'lifecycle', remoteInTrash: R)plus the now-wiredPendingIntentShadowViolationdiagnostic at a LOWER sequence than theRowObserved, so theRowObservedapply freezesin_trashat the settled local target (gated on an OPEN lifecycle conflict for the page — discriminated by decodedconflictKind, sobody-body-delegated's null-propertyIdconflicts do not trigger the freeze). Resolution mirrors property conflicts but on its own path:keep-remotereconverges_nds_row.in_trashto the recordedremoteInTrash(and clears theremote_trashtombstone when the remote target is active);keep-localre-assertsL = !remoteInTrashvia a freshTrashPage/RestorePagepush (the F8 settle handler reconverges on settlement). The whole path is pure full-log replay (the persistedConflictRaisedprecedes theRowObserved, noLrecomputation in the apply path), so reprojection is deterministic. SCOPE: this entry landed theRowObserveddirection (remote restore after local archive); the symmetric remote-trash-after-local-restore (tombstone) direction is now ALSO implemented (see the bidirectional-lifecycle-conflict entry above), so decision 0026 is realized in both directions on the one-shot full-scan path. An open lifecycle conflict stays open until resolved (no auto-close on a remote re-match), mirroring property conflicts. KNOWN LIMITATION:keep-remoteresolves the conflict and reconvergesin_trash, but the settled lifecycle history is unchanged, soreadSettledLifecycleTargetstill reports the old target; re-detection on a later sync re-derives the sameConflictRaisedwhose deterministic idempotency key is deduped (correct steady-state), but if that opening event is ever compacted away the conflict would re-raise — full settled-history reconciliation is deferred. Ratified as decision 0026. -
@overeng/notion-md: Make the media boundary's inert-by-construction invariant explicit and enforced (#775 M3a, decision 0024 Option B). The property-encoding boundary (
external_url|notion_file|local_file) and the byte-backed media boundary (storage.filesNmdFileUnit) are disjoint by type, so external/local property refs never ride the byte path and the media boundary stays durable "by construction". That invariant was previously held only by the current absence of a lowering path; it is now named and pinned. The load-bearing enforcement isclassifyMediaWrite's existing empty-storage.filesrequirement (any unit, regardless ofrole, fails closed — so a future change that lowered a property ref into a byte unit would be caught at the boundary rather than silently transferring bytes), plus a newsync.e2e.test.tsinvariant test proving that pushing a page whose properties carryexternal_url/notion_filerefs persists ZEROstorage.filesunits and classifies asinert. The external-URL durability stance is documented as a known content-fidelity limitation (docs/sync-safety.md+ theexternal_urlencode site): v1 attaches the URL as-is and does NOT verify durability (it can 404/move) — the external analog of a Notion-hosted expiring file URL, with no guard in v1. Behavior-preserving for the already-workingexternal_url/notion_fileproperty writes;local_filestays refused on push. TheExpiringFileUrland datasource-sync planner wiring are a separate later task (M3b). -
@overeng/notion-datasource-sync: Converge
in_trashfrom the settled local lifecycle intent (closes the M2a F8 post-restore and watch-incremental gaps tracked as #698). TheRemoteWriteSettledstore handler now sets_nds_row.in_trash = 1on a settledTrashPageCommandand_nds_row.in_trash = 0(plus clearing the row'sremote_trashtombstone) on a settledRestorePageCommand, in addition to the existing remote-trash tombstone reprojection. This fixes the post-restore staleness bug (a byte-identicalRowObservedis deduped, so without settle-driven convergencepages._in_trashstayed 1 forever after a restore) and the watch-path divergence (the incremental scan never records aremote_trashtombstone, so a local archive onsync --watchpreviously left_in_trash = 0). The convergence is driven by the settle we know succeeded — independent of remote-disappearance classification — so it holds on both one-shot and watch with zero extra API calls (EFF-R01). Remote-initiated trash detection is unchanged (still the tombstone path plus watch's periodic full reconcile). -
@overeng/notion-datasource-sync: Add the observable API call-count budget (#775, decision 0025 Half 1, EFF-R01/R81). A new
otel.e2etest countsnotion.api.requestspans under the one-shot sync span and asserts a falsifiable CEILING (a clean one-shot issues<= 5logical requests) plus the exact read-endpoint multiset (preflightCapabilities,retrieveDataSource,queryRows,listDataSourceViews,retrievePage), so a regression that adds a per-entity read is caught test-side; a no-change re-sync is asserted to issue no write operations. A productionnotion_datasource_api_requests_totalcounter (via@overeng/otel-contractOtelMetric.effect.counter, labelled only by the boundedoperationendpoint) increments alongside the span at each gateway op for fleet visibility. The ceiling counts LOGICAL requests; the rate-limit half (throttle-wait / retry signals in@overeng/notion-effect-client) is a separate change. -
@overeng/notion-effect-client: Add observable rate-limit PRESSURE signals (#775, decision 0025 Half 2). Instrument the genuinely-new
rate_limit_wait_mssignal — the time a logical request is blocked acquiring a throttle token — at theNotionThrottle.applyseam: the wait is measured around theRateLimitergate (clock read outside the gate vs. the instant the token is granted), so it stays behavior-preserving (the limiter still wraps the request exactly as before, token accounting/pacing untouched) and concurrency-correct (each request measures its own wait in its own fiber). The wait is stamped on the request span (notion.rate_limit.wait_ms) and recorded as anotion_rate_limit_wait_mshistogram. Rate-limit pressure is promoted to@overeng/otel-contractOtelMetriccounters labelled by bounded method+operation:notion_http_attempts_total(ACTUAL HTTP attempts incl. retries — distinct from Half 1's logical-request ceiling), andnotion_http_retry_after_total/notion_http_retry_after_ms_total(count + summed ms of server-advisedRetry-Afterbackpressure). A new*.e2e.test.tsdrives a stateful fault-injecting HTTP stub: a 429-then-200 sequence proves the HTTP-attempt count is 2 andretry_afteris recorded with the retry attrs on the span, and a drained-throttle-bucket path provesrate_limit_wait_mslands on the span and histogram. COUNT SEMANTICS (decision 0025): these are the rate-limit-pressure signals, explicitly distinct from Half 1's logical-request budget ceiling. -
@overeng/notion-datasource-sync: Land M2a F8 archive-restore round trip plus lifecycle cleanup. (F8) A
TombstoneRecordedwithreason = 'remote_trash'now reprojects_nds_row.in_trash = 1, so a remotely-trashed row stays a RESTORABLE row instead of reprojecting back toin_trash = 0(which made a 1→0 restore inexpressible). The fake gateway'squeryRowsnow EXCLUDES trashed rows, matching real Notion (a trashed page vanishes fromdata_source.query) — which is what makes the disappearance→remote_trash→F8 reprojection path load-bearing.RestorePageCommandis now guarded symmetrically with the trash path: restoring a moved-out page blocks withMoveOutNotDelete, while a genuinely-trashed page proceeds. The vestigialkind:'lifecycle'convergence branch is removed from the local-surface convergence engine (page lifecycle reaches the planner through CDCrow_archive/row_restoreintents, never the engine). Thein_trashprojection still only converges on the one-shot full-scan path; post-restore and watch-incremental convergence are tracked as a follow-up (#698). -
@overeng/notion-datasource-sync: Close clear-cut #775 follow-ups. (F5) Shared-mode property writes are now gated by the real outbox read-after-write settlement verdict instead of a hard-coded
presentdefault — a second write to a(page, property)whose prior write has not settled blocks withReadAfterWriteMismatch;local/remotemodes staynot-required. (F7)createReplicaSchemainstalls the replica schema and its CDC triggers inside oneBEGIN IMMEDIATEtransaction (with acreateReplicaSchemaInTransactioninner for the projection path that already holds a transaction), so a mid-install failure leaves no partial schema and triggers can never exist without their tables. (F2) Three declared-but-unwired guard names (LinkedDataSourceUnsupported,StoreMigrationBlocked,PageTimestampWakeupOnly) move from the activeGuardNamevocabulary intoreservedGuardNamesso the traceability matrix stops implying test coverage for them, and bodies-on.nmddry-run suppression gains a falsifiable proof (NDS-L4-dry-run-suppression-nmd-bodies). -
@overeng/notion-md: Route datasource-scoped property writes through the shared
@overeng/notion-property-writecore via a new standalone live proof provider (makeStandaloneLiveProof). For a page whose parent is a Notion data source, each writable property is evaluated against a freshly re-read data-source schema + live page (stable property identity, write class, base completeness, relation availability) before the write proceeds; green paths evaluate toallowed()so existing behavior is unchanged, blocked verdicts surface asNmdPropertyWriteBlockedErrorinstead of a silent property update, and asource: remotepage refuses local property mutation as drift. The gateway gains aretrieveDataSourceoperation; standalone (non-datasource) pages keep their current path untouched. Full live schema-drift / relation-completeness coverage (Phase 8) is marked withTODO(phase-8-live-l6). -
@overeng/notion-md: Detect data-source schema drift before a property write via an engine
schema_snapshot(R14, decision 0017, impl-delta Group F). For a data-source-backed page,pullPagenow retrieves the parent data source (GET /v1/data_sources/{id}viapage.parent.data_source_id) and captures the writable property schema into the sidecardata_sourcebinding: a canonical projection of{ name, type, sorted option names }sorted by property name, options only forselect/multi_select/status, hashing names not ids (a rename is id-preserving), excluding ids/colors/descriptions/status-groups/timestamps/created_by/last_edited_by/request_id/computed properties. Before any property write the engine re-retrieves the live schema, recomputes the hash, and on drift refuses withNmdSchemaDriftError(exit 6) rather than risk Notion silently auto-creating aselectoption for an unknown value name — distinct from the exit-7 value/body conflict and not--force-able; resolve by re-pulling. This is the file-engine pathedit --frontmatterreuses (no stateless in-buffer fingerprint, noput --frontmatter; the decision-0013 fingerprint subsystem is gone). Standalone (non-data-source) pages have no snapshot and skip the check. A benign color-only schema change does not trip; the five structural mutations (add/remove/rename/retype property, add option) do. Verified with deterministic projection/drift unit tests + fake-gateway engine tests, and live E2E on real Notion: a row'spage.parentdecodes asdata_source_id, the sidecar binding is captured non-null, a benign property push round-trips, and after a live structural schema mutation a property push refuses with exit 6 without writing. -
@overeng/utils + @overeng/utils-dev: Add the timeless telemetry-foundation VRS (
@overeng/utils/docs/vrs/: vision/requirements/spec + decisions 0001 pure-Effect OTLP front door, 0002 test-shape sibling convergence) capturing the as-builtwithTelemetry/Shape/OtelConfig/sampleResourcesurface, theServiceIdentity→service.{name,namespace,version}resource mapping, and the schema-first@overeng/otel-contractboundary. Records the test-shape convergence OUTCOME: FULL convergence (otelite harness consuming the prodwithTelemetry({ shape: 'test' })front door) is structurally infeasible because@overeng/utils-devis the universal test-harness leaf —utilsandotel-contractalready carrytsc --buildproject references to it, so importing the front door (orServiceIdentity) would close a reference cycle. The maximal clean convergence is applied instead: the harness's all-signals layer now mirrorsshapeDefaults('test')'s 2000 ms shutdown ceiling (previously omitted) with a cross-reference, and test↔prod fidelity stays proven one level up byotel-telemetry.test.tsexercising the real front door against the same otelite receiver. This sibling has a DIFFERENT root cause from the restate./otelsibling (package dependency direction vs the@opentelemetry/apiglobal registry), recorded distinctly. (#771) -
@overeng/megarepo: Fix two
mr store gccorrectness bugs. (1)collectNestedWorktrees(thelistWorktreesref-tree walk) had no depth bound — a worktree-less subtree (a stray dir or a broken worktree whose working tree survived a dropped.git) recursed to the filesystem's depth, the same unbounded-recursion OOM class already fixed inlistRepos. It now caps descent atSTORE_REPO_WALK_MAX_DEPTH(8) ref-name segments underrefs/heads/…(real branch names nest only a few levels, so a legitimate worktree is never truncated), logging a warning at the cap; the.gitworktree check still runs first so a worktree sitting exactly at the bound is found. (2)--dry-runperformed a real mutatinggit fetch --prunein the cold-reclamation path, rewritingrefs/remotes/*in.bareand violating dry-run's no-mutation contract. Dry-run now SKIPS the fetch entirely and classifies against the currently-present (last-known)refs/remotes/*; the TUI banner states remotes were not refreshed. Live (non-dry-run) behavior is unchanged: fetch, and on fetch failure keep all named worktrees with reasonfetch-failed. Regression tests cover both (nested-worktree cap + real-state dry-run no-prune with a paired live-prune discriminator). (#771) -
@overeng/megarepo: Migrate the last raw
Effect.withSpansites in product code (the inline gc/git lib spans instore-archive,store-fs-atomic,store-gc-config,store-gc-observations,store-lossless,store-pr-state,git, andstore/mod.ts— the gc helper modules were already schema-first) onto schema-firstOtelOperationcontracts defined inlib/observability.ts, so all megarepo telemetry now routes through the@overeng/otel-contractDSL. Span names,span.labelexpressions, and attribute keys are reproduced byte-identically (including the intentionally unprefixed keysbranch/reason/path/repoRoot/worktreePath/worktreeHead/store.repo/store.bare_repo.path); the streaminggit/cmdoutput-byte counters are untouched (no buffering reintroduced). With the megarepo product tree clean,overeng/no-raw-otel-primitivesis now a strict zero-allowlist invariant there — theraw-otel-boundaryboundary test is green with no megarepo entry inallowedRawOtelFilesand no megarepo exemption in the oxlint config. (#771) -
@overeng/megarepo: Migrate the
mr store gcRSS gauge + sampler + telemetry test onto the telemetry foundation primitives. Themegarepo_store_gc_rss_bytesgauge is now defined through the schema-firstOtelMetric.gaugecontract (branded name +repo_concurrencylabel cardinality enforced) and emitted via theOtelMetric.effect.gaugebridge'strustedSet(schema-encoded label, no rawMetricLabel); the hand-rolledEffect.withClock(Clock.make())+Schedule.spacedsampler is replaced by thesampleResourceprimitive, which owns the real-clock tick and the telemetry-off no-op gate — so the call site instore/mod.tsdrops itsEffect.serviceOption(OtelConfig)double-gate (theOtelConfigrequirement is discharged insidesampleStoreGcRss, keeping it out of the gc command'sRso the bulk suites stay layer-free). The OTEL instrumentation test moves from a hand-rolledOtelite.capture+ manualinspectloops toOteliteTestHarness.runInProcessAllSignals+ theexpectTrace/expectMetricsDSL, asserting the same contract (six gc phase spans, agit/cmdspan withgit.output.bytes, and the RSS gauge value>0 with arepo_concurrencylabel) and staying non-vacuous. Metric name + label semantics are unchanged. (#771) -
@overeng/restate-effect: Bind the
./otelbridge's service identity to the shared@overeng/otel-contractbrands.service.nameis decoded throughOtelServiceName(andservice.versionthroughOtelServiceVersionwhen set) at the resource-construction edge — the one place restate's raw config strings become branded — so restate obeys the SAME naming law as the CLIs (makeOtelCliLayer) and a malformed name is a defect at the composition root rather than a silent backend surprise. Consistency-only: the runtime mechanism (NodeTracerProvider+ global registration,unwrapScoped, theacquireRelease(provider.shutdown())flush finalizer) is unchanged, and the publicOtelResourceConfig/layerConfigplumbing stays rawstring. restate sets name + version, notservice.namespace, so no namespace binding is added. (#771) -
@overeng/restate-effect + @overeng/utils: Document restate's
./otelprovider-registration layer (NodeTracerProvider+provider.register()+ globalAsyncLocalStorageContextManager) as a DELIBERATE, structurally-required SIBLING of the sharedwithTelemetry/makeOtelCliLayerfoundation — not a convergence gap. Restate's hook/bridge/observer read the active span through the@opentelemetry/apiGLOBAL (trace.getActiveSpan()), so restate needs a globally-registered SDK provider; the shared path is pure-EffectOtlp.layerJson, which never touches the global registry. Bolting an SDK provider onto the sharedserviceshape would double-export againstOtlp.layerJson(split/duplicated traces) and drag@opentelemetry/sdk-*+ a process-global onto every CLI consumer. Recorded in restate-effect decision 0007 with a one-line cross-reference comment at theOtlp.layerJsonsite in@overeng/utils/src/node/otel.ts. The sharedServiceIdentitystruct from@overeng/otel-contractwas NOT adopted at restate's identity-decode edge: the struct REQUIRESservice.namespace(and a non-optionalversion), which restate does not set, so restate keeps decoding the individualOtelServiceName/OtelServiceVersionbrands — adopting the struct would force a namespace invention / public-API change deliberately deferred. Docs/comment only; no runtime, type, or public-API change. (#771) -
@overeng/utils + CLIs: Migrate the remaining CLIs (genie, notion-md, notion-cli, workflow-report, notion-datasource-sync) onto the typed front door
withTelemetry({ identity, shape: 'cli', endpoint }), each building a brandedServiceIdentityat its composition edge (so a raw-string service name is atscerror). notion-datasource-sync's dynamic per-subcommand name folds into the branded path. With every consumer now onidentity, the legacyserviceName: stringoption is removed frommakeOtelCliLayer(clean break —identityis now required; no shim). (#771) -
@overeng/otel-contract + @overeng/utils: Add a typed
ServiceIdentity(brandedOtelServiceName+OtelServiceNamespace+OtelServiceVersion) and accept it onmakeOtelCliLayervia a newidentityoption that stampsservice.{name,namespace,version}onto the OTLP resource of ALL signals (traces, metrics, logs).OTEL_RESOURCE_ATTRIBUTES/OTEL_SERVICE_NAMEenv inheritance is preserved (explicit identity wins on collision, env-only attrs survive — covered by a new otelite-capture test). The legacyserviceName: stringoption still works (now optional; it will be@deprecated+ removed once the remaining CLIs migrate toidentity);mr.tsis migrated to the typedidentityas the reference consumer (a raw-string name is now atscerror on that path). The other five CLIs keepserviceNameand compile unchanged. Tradeoff: providing neitheridentitynorserviceNameis a runtime defect (Layer.die), not a compile error. (#771) -
@overeng/utils-dev:
makeOtelVitestLayernow builds its in-process OTLP exporter viaLayer.suspend(wasLayer.unwrapEffect(Effect.sync(...))), matching prodotel.tsand using the scope-correct primitive for a lazily-built exporter. A new test (otel-vitest-flush.test.ts) locks the flush-on-shutdown guarantee: with a 60s export interval the span only lands AFTER the layer scope closes (pre=0 / post>0). (#771) -
@overeng/utils + @overeng/megarepo: Make OTLP shutdown-flush config-free.
makeOtelCliLayer'sshutdownTimeoutis now a safety ceiling with a TTY-aware default (30s non-interactive / 10s interactive TTY) instead of a flat2000, andmr.tsdrops its50msoverride. The flush is a scope finalizer Effect awaits to completion, so the cap never adds latency on a healthy collector (exit ≈ one round-trip) and only bounds the worst case when the collector is black-holed; a too-small override interrupts the in-flight export and silently drops the final batch (the real bug behind "the gauge sometimes didn't land"). Export intervals are mid-run granularity only — the final batch flushes on scope close regardless. (#771) -
@overeng/tui-react:
runTuiMainnow exits 130 on signal interruption (Ctrl-C), the shell convention, via a customrunMainteardownthat honors theprocess.exitCode = 130the interrupt branch sets (the default teardown forced 0 on a signal, clobbering it); uncaught failures still map to 1, success to 0. The shutdown-flush + exit-code contract is captured indocs/spec.md(Lifecycle & Cleanup) anddocs/.decisions/0001-shutdown-flush-contract.md, and locked by a fast deterministic regression test (test/integration/cli-shutdown-flush.test.ts): natural exit awaits + lands the flush and is fast, a signal exits 130 and bails fast, success exits 0. (#771) -
@overeng/utils-dev/otelite: Resolve the
otelitebinary fromOTELITE_BINbefore falling back toPATH, and document the plain-shell Nix workflow for focused wrapper tests. -
@overeng/otel-contract: Add a schema-first GAUGE primitive to the
OtelMetricfamily, mirroring counter/histogram:OtelMetric.gaugebrands the metric name (OtelMetricName), takes a low/bounded-cardinality label schema with the same cardinality enforcement, andOtelMetric.effect.gaugeexposes a typed EffectMetricruntime bridge whoseset/trustedSetdrive a gauge up and down (Metric.seton a taggedMetric.gauge). NewOtelGaugeDefinition/OtelEffectGauge/OtelEffectGaugeMetrictypes and aninstrument: 'gauge'metadata kind. This is the sanctioned path for the existingmr store gcRSS gauge; banning rawMetric.gauge/Metric.setinno-raw-otel-primitivesis deferred to the Phase-2 consumer migration so the rule flip lands atomically with the only raw-gauge call site. (#771) -
@overeng/utils: Add the principled telemetry front door.
withTelemetry/TelemetryLayer({ identity, shape, endpoint })wrapsmakeOtelCliLayerwith aShape ∈ {cli, service, test}that picks the correct export/flush defaults per CLI vs long-lived service vs deterministic test — so consumers select intent, not raw interval/timeout knobs. AsampleResource(+sampleGauge) primitive generalizes the per-consumer RSS-gauge sampler: it forks a fiber that ticks on REAL wall time (Effect.withClock(Clock.make())) and is a NO-OP when telemetry is off (gated on theOtelConfigmarker viatelemetryEnabled/whenTelemetryEnabled), so no consumer re-derives the clock-decoupling or the double-Optiongate.makeOtelCliLayer+ the legacyserviceNamepath stay intact and exported. (#771) -
@overeng/utils-dev/otelite: Extend the in-process otelite capture layer from traces-only to ALL THREE signals (traces + metrics + logs), wired via the combined
Otlp.layerJson(correct per-signal URL suffixing) underLayer.suspend. A newrunInProcessAllSignalsharness captures and asserts spans + metrics (counter/gauge) +Effect.loglines in one program against an ephemeral otelite receiver, so the shippedexpectMetrics/expectLogsmatchers are finally usable in-process (previously they had no in-process emission path and metric/log tests had to hand-roll the prod exporter). (#771) -
@overeng/otel-contract: Add branded/refined OTEL name schemas (
OtelAttributeKey,OtelSpanName,OtelMetricName,OtelServiceName), validate contract names/keys at definition time, add an EffectMetricruntime bridge for schema-first metric contracts, and extend the raw-OTEL lint rule to ban raw EffectMetric.*APIs outside approved contract/test boundaries. -
OTEL devenv module: Stop requiring the retired legacy
otelCLI inOTEL_MODE=system; dashboard refresh is now best-effort when a compatible legacy CLI is present, while shell setup still succeeds with the shared system stack. -
@overeng/otelite-effect → @overeng/utils-dev/otelite: Folded the standalone
@overeng/otelite-effectwrapper package into@overeng/utils-devas a new./otelitesubpath export and deleted the standalone package. The wrapper is a dev/test util with no non-test consumer, andutils-devalready declares every dependency it needs (@effect/platform,@effect/platform-node,@effect/opentelemetry,@effect/vitest) and already uses subpath exports — co-locating it removes autils-dev ⇄ otelite-effectcycle. The source moved topackages/@overeng/utils-dev/src/otelite/and the tests (incl. the D1 wire-level e2e) tosrc/otelite/*.test.ts. The public API is unchanged; consumers now import from@overeng/utils-dev/otelite. TheOteliteservice tag and theOtelite*error tags are renamespaced to@overeng/utils-dev/otelite/*. The tests still run the real nix-builtotelitebinary onPATH(provided by the dev shell). Seecontext/otelite/decisions/0015. -
CI / Nix packages: Refresh the stale
genie,megarepo,notion-md, andtui-storiespnpm fixed-output hashes after the schema-first OTEL contract change updated the workspace dependency closure. -
@overeng/notion-md: Move the v-next public CLI to
track/status/sync:trackis now the only page-id bootstrap command,sync/statusoperate on local self-describing files, write-capable paths support--dry-run, andsync --watchroutes through the same source-aware reconcile engine as one-shot sync. -
@overeng/notion-md: Add explicit v-next destructive sync modes for unsupported-block deletion (
--allow-delete-unknown-blocks) and literal Roughdraft markup writes (--allow-review-markup), validate referenced object-store payloads on v-next reads, and addsync --gc-objectswith dry-run planning for unreachable content-addressed objects. -
@overeng/notion-md: Complete the v-next source-dispatched sync contract by requiring explicit
.nmdsource, removing legacy sync helpers from the public package surface, preserving watch as a first-class reconcile path, adding live watch and OTEL span verification, and aligning VRS/user docs with the stateless Mirror Sync vs base-backed Shared Sync split. -
@overeng/notion-md: Refresh the fidelity corpus from live Notion through repeatable capture tooling, add live corpus verification, and fold Notion's lossless code-fence language alias expansion (
js/ts→javascript/typescript) into the semantic-equivalence oracle while keeping JavaScript and TypeScript fences distinct. -
@overeng/notion-md: Add schema-decoded Notion webhook trigger ingestion for watch mode. Page webhook payloads normalize to secret-safe trigger signals and feed the existing batch watch queue as
webhookreasons; comment events are decoded and classified as an explicit non-body boundary until comments API/client support exists. -
CI / Nix packages: Refresh the
oxc-config,genie,megarepo, andnotion-clipnpm fixed-output hashes to the values observed by PR #780 CI/local proof so lint, typecheck, test, and FOD validation jobs can realize their Nix dependencies again. -
@overeng/megarepo tests: Make the missing-remote sync error fixture deterministic by using a local
file://clone failure instead of a fake GitHub SSH remote, and give the heavier cold store-GC liveness/archive integration case its own timeout budget. -
@overeng/restate-effect tests: Run Vitest files serially for the native
restate-serverintegration suite so full-repo checks do not cross wires between concurrently booted SDK deployments. -
CI / Nix packages: Refresh the stale
workflow-reportpnpm fixed-output hash so the Storybook preview reporting step can build#workflow-reportagain after the branch rebase updated the workspace dependency closure. -
@overeng/restate-effect: Made
Restate.run's type HONEST. A durablectx.runstep carries NO catchable typed failure: the inner effect runs viaRuntime.runPromiseinsidectx.run, so a typedEffect.failonly REJECTS the step (Restate retries; a give-up maps to aRestateErrorDEFECT) and never reaches the outer failure channel — the oldrun<A, E, R>(…): Effect<A, E, …>advertised a typedEthatcatchTag/catchAllwould typecheck against but that could never fire.runis nowrun<A, R>(name, effect: Effect<A, never, R>, options?): Effect<A, never, …>, andrunExitisrunExit<A, R>(…): Effect<Exit<A>, never, …>— the honest OBSERVATION form, whose failure channel isnever(an observed failure is a defect/interruptCause, not a phantom typedE). Domain errors now belong in the HANDLER body (classify the step's result there) or are encoded as VALUES inside the step; to force a durable retry, DIE inside the step. A passed typed-Einner effect is now a COMPILE error (negative-type assertion incapability-inference.types.ts). Callers reconciled: the saga integration test's failingpaystepEffect.dies (wasEffect.fail), andexamples/12-self-reschedule.ts'spollComposedSourcereturns a tagged VALUE withE = never(classified in the cycle body, unchanged).examples/14-http-error-classification.tsalready used the die-the-step / classify-in-body strategies; only its prose was corrected. VRS: decision 0003 (#4 — corrects the earlier "keep the innerEflowing throughrun"), 03-effect-runtime / 04-error-boundary specs, the guide handbook, and a DEFERRED typed-failure-transportrunnote (an encodedfail(E)journaled via an error schema). No dependency changes. -
@overeng/restate-effect: Centralized the contract-invocation policy into ONE boundary (
clients/InvocationPolicy.ts, decision 0020), fixing two P2 client bugs by construction + the architectural root cause behind them. The annotation-derived transport facts — input/output serde (incl. theRestate.sensitive-field redaction transform), theRestate.idempotencyKey-field extraction, and the SDK opts bag — were previously assembled SEPARATELY in every adapter (endpoint materialization, each ingress client, the in-handler service-to-service clients, AND the testing harness, which built its own paralleleffectSerde), so annotation support was partial by construction. Fixed P2 bugs: (1)RestateIngress.call(stateless Service) built its serdes WITHOUT theRedactionCipher, so a contract with aRestate.sensitivefield was un-callable through ingress (the encode threwRedactionCipherMissingError) even thoughobjectCalland the served handler both encrypted it; (2) the Servicecallpassed no idempotency key, so aRestate.idempotencyKeyinput field did NOT dedupe a retry (unlikeobjectCall/objectSend). Now EVERY adapter — endpointhandlerOpts, all ingress paths (call/objectCall/objectSend/workflowSubmit/workflowAttach/workflowOutput/workflowCall/result/resolveAwakeable), the in-handlercallRpc/sendRpc, AND the harness ingress +stateOf— consumes the one policy, so an annotation behaves consistently at every public entrypoint andRestateIngresscarries the optional cipher (resolved from aRestateRedactionlayer in context).materialize*now also VALIDATES annotation placement and FAILS LOUDLY whenRestate.idempotencyKey/Restate.sensitiveis applied to the input STRUCT instead of a FIELD (a silent no-op otherwise), or when two fields carryRestate.idempotencyKey. Covered server-free (Annotations.test.tsplacement validation;options-surfacing.test.tsmaterialize-rejection) and against a nativerestate-server(clients/contract-policy.integration.test.ts: the invariant matrix over Service × Object × Workflow / call·send·attach —sensitiveround-trips encrypted,idempotencyKeydedupes, terminal error decodes — verified to FAIL on the pre-fix code for both P2 findings). VRS: decision 0020, 02-schema-serde / 05-clients / 09-testing specs. No new dependencies; all 164 package tests green. KNOWN LIMITATION (since FIXED, see the later Fixed entry): the synchronous in-handlercallDescriptor(used insideRestate.all([...])) cannot resolve the ambient cipher at construction time, so asensitive-field redaction on a descriptor-issued peer call was not applied. Now resolved by threading the cipher throughDescriptor.issueat issue time. -
@overeng/restate-effect: Internal
src/reorganized into ten named subsystem subdirs (authoring/,schema/,runtime/,error/,clients/,scheduling/,endpoint/,observability/,testing/,admin/) so the code layout mirrors the VRS subsystem taxonomy.Endpoint.tsis split into the error-boundary classification + capability-marker machinery (error/Boundary.ts:classifyOutcome/toTerminal/provideHandlerCaps/ theBoundary*types) and the serving/materialize layer (endpoint/Endpoint.ts); the shared bare admin client (AdminApi.ts) moves underadmin/. Pure refactor:mod.tsstays the root barrel and the public export surface is unchanged —././otel/./testing/./adminresolve to the same exported symbols (thepackage.jsonexportsmap now points at the new internal paths). No behavior change; all 134 tests green. -
@overeng/restate-effect: VRS design docs restructured into the ten numeric subsystem dirs (
docs/vrs/01-authoring/…10-admin/), mirroring thesrc/subsystem taxonomy.docs/vrs/spec.mdis now a thin architecture index (Status + Scope + the architecture diagram + the cross-cutting Deferred + Open-design-question lists + a table linking each subsystemspec.md); the §-section bodies moved into each subsystem'sspec.md, and the requirement bullets distributed into each subsystem'srequirements.mdPRESERVING the global IDs (R01–R39 / A01–A11 / T01–T08 — one ID per subsystem, never renumbered, so every cross-reference still resolves). Rootrequirements.mdkeeps only the cross-cutting faithful-binding stance + the global Assumptions/Tradeoffs;vision.md+glossary.mdstay whole at root (inherited downward).docs/vrs/decisions/renamed todocs/vrs/.decisions/(dot-prefixed per/sk-vrs;git mvpreserves history, all 0001–0019 records unchanged in content). External path references updated to the new locations (README.md,docs/guide/*,src/schema/{Serde,Annotations}.tsdoc-comment links, intra-VRS cross-links, epic #757). Docs-only — no library surface change. -
genie/ci-workflow: Skip workflow-report PR comment publishing for fork pull requests after writing the job summary, so preview-reporting jobs do not fail when GitHub downgrades the
pull_requesttoken to read-only. -
@overeng/genie: Give the entry-purity compiler test an explicit timeout so Linux CI load does not fail the semantic guard under Vitest's default 5s test timeout.
-
@overeng/notion-md / @overeng/notion-datasource-sync: Address PR review regressions for datasource body/property settlement and workspace establishment. Verified body-push settlement now preserves existing
.nmdwritable frontmatter properties,--sqlite data/v1/<source>.sqliteselects the matching manifest source in multi-source workspaces, andtrackwritesnotion.workspace.v1.jsononly after successful establishment. Public SQLite replicas now treat macOS/varand/private/vartemp-path aliases as the same workspace for move detection, avoiding falsemovedstatus while still detecting copied data files. Also makes GFM autolink canonicalization idempotent for generated canonical bodies. -
@overeng/notion-datasource-sync:
keep-localresolution of a lifecycle conflict now STAYS FROZEN until the re-assert settles, closing a durable silent last-writer-wins hole (#775 M2a'-2, XC-R02). Previouslykeep-localfullyresolvedthe conflict and enqueued a re-assert (TrashPage/RestorePage); if the remote had actively diverged (e.g. it restored the page), that re-assert push BLOCKS (StaleSurfaceBase/tombstone-safety) and never settles. On the next sync, re-detecting the SAME local/remote divergence produces a byte-identical idempotency-keyedConflictRaised(key =conflict:<surface>:<localHash>:<remoteHash>, lifecycle hashes are independent ofpropertiesHash) that dedups against the already-resolvedconflict, so the freeze gate saw NO open lifecycle conflict and theRowObservedapply silently flipped_nds_row.in_trashto the remote value — the exact XC-R02 violation the feature prevents. Fix ("stay frozen until the re-assert settles"): a newresolvingconflict state (schema v8, migrated by recreating the pure-projection_nds_conflictCHECK) —keep-localon a lifecycle conflict moves toresolving(NOTresolved) and keeps its re-assert enqueue; the#hasOpenLifecycleConflictfreeze gate (and the compaction-safety + status-count surfaces) treat BOTHopenANDresolvinglifecycle conflicts as freeze-active, soin_trashstays at the local targetLwhile the re-assert is pending/blocked; theRemoteWriteSettledF8 handler transitionsresolving→resolvedONLY when the re-assertedTrashPage/RestorePagegenuinely settles (L re-established remotely, divergence gone), replay-deterministically by piggybacking the in_trash reconvergence. A re-detected same-divergenceConflictRaisedstill dedups, but because theresolvingconflict keeps the freeze active there is no silent flip — the conflict stays visibly unresolved.keep-remoteis unchanged (still resolves and reconverges to the remote target). SUPERSEDES the M2a'-2 KNOWN LIMITATION aboutkeep-local(the re-detected-after-resolution case is now safe by construction, not merely deduped). Covered by a kill-test (keep-local + blocked re-assert + second sync with a novelpropertiesHash: conflict count stays 1, stateresolving,_in_trashstays at L, no silent flip), a settle test (re-assert settles →resolving→resolved), and replay-determinism + keep-remote regression tests. -
@overeng/notion-md: Make the SM6.2 comment-write boundary mutation-implying instead of presence-based. The guard previously blocked any push/pull/shared write to a page that merely HAS comments — including a body-only edit — but
updateMarkdown({_tag:'replace_content'})writes only body content and is structurally incapable of creating/editing/resolving a Notion comment, so that block was fictitious and over-blocked legitimate body edits on commented pages.classifyCommentWritenow compares the comment inventory the write would PRODUCE against the current inventory and blocks only on a genuine add/remove/modify; for today's body-only write pathsproduced === current, so a body edit on a commented page proceeds (pushed/pulled/shared-merged) and the namedCommentWriteUnsupportedguard is a dormant fail-closed gate that trips only when a real comment-mutation path is ever wired. Thesharedcomment guard moved from the top ofreconcileSharedFileinto themerge/forcewrite branches (symmetric to how single-source guards scope to push/pull), so thenoopshared branch no longer evaluates it. Block-detection coverage lives at the classifier-unit level (the dormant block path is not reachable end-to-end today); theNmdNonBodyWriteBlockedError.fileIdsJSDoc now documents that the field carries the offending unit ids — file or comment — discriminated byguard. -
@overeng/notion-effect-client, @overeng/notion-md: Canonicalize hosted-media URLs so media-bearing bodies are idempotent and pushable (R36, decision 0007). Notion-hosted media (
type: "file") renders with an expiring signed S3 URL whoseX-Amz-*/signature/Expiresquery params rotate on every pull, making the rendered body hash volatile (breakingcat→putidempotence and staling base hashes with zero edits) and causingupdate_content/replace_contentpushes on media pages to be rejected by the post-pushsemanticEquivalentgate. A sharedcanonicalizeMediaUrl(andcanonicalizeMediaUrlsInMarkdown) innotion-effect-clientstrips only the volatile signature/expiry query-param family by name (origin + path + any benign params kept). The renderer applies it ingetBlockUrl's Notion-hostedfile.urlbranch so pull/catoutput is deterministic;canonical-markdown.tsapplies the identical function insidecanonicalizeBlockMarkdown/semanticEquivalentso the gate compares the same canonical form. External (stable) URLs — including ones carrying a benign query param — are left untouched. Verified with deterministic unit tests (signed→canonical, rotated-signature equality, external-with-query untouched) and live E2E on real Notion: a hosted+external-media page's body hash is stable across two no-op pulls and a no-op push is not rejected by the post-push gate. -
@overeng/notion-core, @overeng/notion-md: Fix silent body-data loss on push for renderable-but-not-round-trip-safe blocks (R38, #785). The body-fidelity classifier (
classifyBodyCompleteness) previously flagged only API-unsupportedblocks, sochild_database([embedded db]()),table_of_contents([TOC]),synced_block,child_page-in-body, and degradedbookmark/embed/link_preview/breadcrumb/link_to_pageclassifiedcomplete. They render to Markdown that Notion re-parses as a plain paragraph on push, so editing an unrelated paragraph and pushing silently destroyed the untouched block (live-proven on real Notion; affected filesync, not just the planned editor). The classifier now also flags a curated set of known not-round-trip-safe types (criterion: body-Markdown rendering does not reparse to the same block — a type set because notion-core is pure and the endpoint/independent renderings agree, so a suffix heuristic can't catch them), surfaces the offending types in the lossy verdict, and the shared refusal gate (assertRemoteMarkdownComplete) refuses such pages at the pull on every surface (cat/put/edit/filesync) with a message naming the block class and pointing to the Notion UI (NmdRemoteBodyLossyError, exit 3).child_pagehas a dual role: a child page that is a tree node (its own.nmdfile) is tolerated viatolerateTreeChildPageson the tree path while still being refused as a single page's body block, and any other lossy block on the same tree node is still refused. Verified with deterministic classifier/gate unit + fake-gateway tests and live E2E (lossy page refused at pull, representable page still round-trips). Hosted/external media stays representable (decision 0007). -
@overeng/notion-cli: Fix the umbrella
notionbinary crashing at startup for every command withReferenceError: Cannot access 'createTuiApp' before initialization(#787).runRootCli(cli.ts) imports the three command trees concurrently viaPromise.all, and each schema/db renderer'sapp.tsbuilt its*Appby callingcreateTuiApp(...)as a module-load side-effect. Under Bun's concurrent async module evaluation that top-level call reached the shared@overeng/tui-reactmodule graph while it was still mid-initialization, so a re-exported binding (createTuiApp, then its body'screateInterruptedAction, …) was in the temporal dead zone. Root cause is the module-load side-effect, not a barrel export-order/TDZ bug and not a circular import (verified: the only barrel self-imports are JSDoc; convertingcreateTuiAppto a hoisted function only relocated the crash to the next top-level const). Fix: all five rendererapp.tsmodules (Diff/GenerateConfig/Generate/Introspect/Info) now construct their*Applazily via a memoizedget*App()accessor instead of at module top level, so nocreateTuiApp(...)runs during import; the sharedtokenOption/resolveNotionTokenhelpers also moved out ofschema/mod.tsso the concurrently importeddbtree no longer reads a schema export while that module is still evaluating. The memoization preserves the previous single-instance (one registry/atom set) semantics. Confined tonotion-cli— no change to shared@overeng/tui-react. Regression test (src/concurrent-import.unit.test.ts) spawns the umbrella's concurrentPromise.allimport path under Bun (the binary's runtime) and asserts no TDZ crash; verified RED on the pre-fix code and GREEN after. -
@overeng/notion-effect-client: Restrict
canonicalizeMediaUrlsInMarkdownto known Notion-media hosts so an external signed URL is preserved (PR #786 review, P1). The Markdown-string canonicalization path — run bycanonicalizeBlockMarkdownon both pull and push — previously stripped the volatileX-Amz-*/signature/Expiresparams from any signed-looking URL by param name, so an external private-S3 image embedded by URL would lose its load-bearing credentials and, because canonicalization now runs on both wire boundaries, could be surfaced and then persisted broken. Unlike the renderer (getBlockUrl, which only canonicalizestype: "file"URLs), the string path cannot seefilevsexternal, so it now gates on a Notion-media host allowlist (prod-files-secure.s3.us-west-2.amazonaws.com,file.notion.so,*.notion.so,*.notion-static.com, and the olders3.../secure.notion-static.com/...bucket-in-path form); non-Notion hosts are left untouched.canonicalizeMediaUrlitself stays host-agnostic (the renderer feeds it only known-hostedfileURLs, so its idempotence guarantee is unchanged). Regression test: an external signed S3 URL on a different bucket host inside Markdown is preserved verbatim. -
@overeng/notion-effect-client: Declare
@overeng/utilsas a runtime dependency (PR #786 review, P2).config.tsimportssha256Hex(notionTokenFingerprint) from@overeng/utilsin production code, but the package listed it only as a dev/peer dependency, so a standalone install could fail to resolve it at runtime. MovedutilsPkginto the generated runtimedependencies(package.json.genie.ts+dt genie:run). The regeneration also reconciled a pre-existing stale generatedpackage.json— a leftover externalpeerDependenciesblock the current genie source no longer emits — which had drifted the lockfile and broke the frozen-lockfile Nix build. -
@overeng/notion-cli: Apply the editor exit-code teardown to the umbrella
notion editalias (PR #786 review, P2). The standalonenotion-mdbinary maps tagged editor failures to the scriptable exit codes (3 lossy / 6 schema-drift / 8 abort, …) via arunMainteardown, but the umbrellanotionroot used the default teardown, sonotion editcollapsed those to the generic exit 1 — diverging fromnotion-md editfor scripts. The umbrella now wires the sameeditorExitCodeteardown; safe for the non-editor commands (editorExitCodefalls back to 1 for any unmapped failure and 0 on success, matching the previous default), with Ctrl+C now mapping to 130 consistent with the standalone binary. Runtime e2e of the umbrella binary remains gated on #787. -
@overeng/megarepo:
mr store gc/mr store statusno longer OOM the host on a worktree with a large untracked tree (decision 0007, bounded memory). The dirt path (git status --porcelain --untracked-files=all) previously buffered the full subprocess output through an O(n²) per-chunkUint8Arrayconcat (10.3 MB / 64k untracked files → 269 MB peak; 80 MB → OOM); undermr store status' 64-way fan-out that compounded to multi-GB. Now:runGitCommand's output concat is a single O(n) allocation, and the three unbounded parsers (getWorktreeStatus/getWorktreeRemovalStatusdirt count,listWorktrees,revListUnpushed) fold their stdout line-by-line through a streamingSinkat constant memory via a newstreamGitCommandLineshelper (which preservesGitCommandErrorexit-code/stderr semantics — nakedCommand.streamLinesdrops both).changesCountstays EXACT (count Sink), so verdicts are byte-for-byte unchanged. A new subprocess-isolated memory regression (git-memory.integration.test.ts) asserts the dirt path stays under a constant per-process RSS growth — it FAILS on the old O(n²) code (~225 MB) and passes on the streaming path (~40 MB) (#771). -
@overeng/megarepo:
mr store gcis now instrumented and its per-repo concurrency is tunable, so the memory↔throughput operating point can be fixed experimentally via OTEL (decision 0007). Per-repo concurrency is read fromMEGAREPO_GC_REPO_CONCURRENCY(default 4, fixed by the OTEL sweep — see the operating-point entry below; back-pressure stays structural via boundedStream.mapEffect, and reconcile-all + per-worktree locks keep cross-megarepo safety regardless of the value). New phase spansmegarepo/store/gc/{collect-liveness,list-repos,collect-worktrees,resolve-pr,cold-reclaim,legacy-sweep}carryrepo.count/worktree.count/repo_concurrency; each git subprocess gets agit/cmdspan withgit.output.bytes/git.output.lines(from scalar streaming counters, never a buffer); and amegarepo_store_gc_rss_bytesgauge samplesprocess.memoryUsage().rssacross the run, labelled byrepo_concurrencyso a sweep yields one comparable series per operating point. The RSS sampler fiber forks only whenOTEL_EXPORTER_OTLP_ENDPOINTis set (zero overhead otherwise). Progressive gc dispatch no longer copies the whole results array (results: [...results]) on every batch — the read-only accumulator is passed by reference; the-o jsondocument schema is unchanged. Verdicts are unchanged at any concurrency (#771). -
@overeng/utils + @overeng/megarepo: Resolve the OTLP endpoint at the composition root instead of reading
process.envambiently in library/command code, so tests need ZERO OTEL env handling (decision 0007).makeOtelCliLayerbecomes a pure function of an explicitendpoint: Option<string>(when provided it is authoritative and the layer never readsprocess.env; an env fallback is retained for callers not yet migrated) and staysLayer.suspendso exporter finalizers still flush on shutdown. A newotelEndpointFromConfighelper resolves the endpoint via EffectConfig.option(Config.string('OTEL_EXPORTER_OTLP_ENDPOINT'))at the binary edge; the layer also provides the resolved endpoint as anOtelConfigcontext tag, which themr store gcRSS-sampler gate now reads viaEffect.serviceOptionrather thanprocess.env['OTEL_EXPORTER_OTLP_ENDPOINT']. The megarepo bulk test suite is now hermetic by construction — it runsmrCommandwithoutmakeOtelCliLayer, so no exporter exists and the sampler gate is a no-op regardless of any ambient endpoint — and the priordelete process.env.OTEL_EXPORTER_OTLP_ENDPOINTworkaround in the test setupFile is removed (verified: env set + no test handling ⇒ zeroPOST /v1/{traces,metrics}reached a listener, full suite green, no hang). The otelite capture test passes its receiver's endpoint explicitly into the layer (no env mutation). This is a reusable cross-project pattern for any env-derived config; the othermakeOtelCliLayercall sites (genie, workflow-report, notion-datasource-sync, notion-md, notion-cli) keep the env fallback and can migrate incrementally (#771). -
@overeng/megarepo: Fix the gc operating point at
MEGAREPO_GC_REPO_CONCURRENCYdefault 4, proven by an end-to-end OTEL sweep (decision 0007). On a 1200-worktree isolated store with agh-latency shim, wall time was 25.2 s → 11.4 s (2.2×) from concurrency 1 → 4; 4 → 8 added ~7% and 8 → 32 sat at the run-to-run noise floor, while peak process-tree RSS stayed ~537 → 633 MB (flat, sub-GB) — throughput is the binding constraint and its knee is 4. Two latent instrumentation defects were fixed along the way: (1) themegarepo_store_gc_rss_bytessampler coupled itsSchedule.spacedcadence to the ambient clock, so a zero-sleep decision clock (in tests) turned it — and the OTLP exporter's reader fibers — into a hot loop; the sampler now ticks on real wall time viaEffect.withClock(Clock.make()), decoupled from gc's decision clock. (2) The exporter'smetricsExportIntervalis now tunable (@overeng/utilsmakeOtelCliLayer) and set to 1 s in themrbinary, so the gauge flushes within a short run instead of being undersampled by the 10 s default. A new in-process otelite-capture test (src/cli/store-gc-otel.integration.test.ts) stands up an ephemeral OTLP receiver, runs gc through the real exporter, and asserts the six phase spans, agit/cmdspan carryinggit.output.bytes, and the RSS gauge (value > 0,repo_concurrencylabel) all land — non-vacuous (RED when the sampler is stubbed or a phase renamed). Bulk verdict tests stay hermetic w.r.t. OTLP; telemetry-asserting tests opt back in with their own receiver (#771). -
@overeng/megarepo: Cold
mr store gcnow never reclaims a repo's default branch (read offline from the bare'sHEAD), independent of PR state or liveness. Dry-run validation against the real store found a vendored dependency'smainworktree archive-eligible via aheadRefName=mainPR-join false-positive while not in any recorded live set; the guard (keep reasondefault-branch, decision 0004) closes themain/masterhazard as a belt-and-suspenders complement to the cross-megarepo veto (#771). -
@overeng/megarepo: Harden the cold named-branch
mr store gcreclamation path against an adversarial review. (1) Archive freed no branch for production worktrees: a productionrefs/heads/*worktree is NON-DETACHED, so aftergit worktree movethe moved worktree still has the branch checked out andgit branch -Dis REFUSED (cannot delete branch used by worktree), leaving the branch unfreed and a latermr applyre-add broken (invariant 4). Fixed by detaching the moved worktree's HEAD (git checkout --detach, newGit.detachWorktreeHead) before freeing the ref; covered by a new integration test using a non-detached worktree (the prior fixtures only used--detach, masking the defect). (2) Liveness registry writes were non-atomic:refreshWorkspaceRegistryand the under-lock reconcile rewrite used plainwriteFileString, so a torn read during a concurrent gc reconcile could drop a workspace's live-set veto (decision 0002 hard veto); both now route throughwriteFileAtomic. (3) Partial-archive mis-reporting: once the move succeeded the result is nowarchivedwith the realrecoverPath(post-move branch-free + README steps are best-effort-but-reported via a warning), instead of falsely reportingerror/"left intact". The.archive/README.mdappend is now an atomic write. New tests cover the archive-time live-set veto re-check,loadStoreGcConfigfile load + corrupt-file degradation, a CLOSED-PR archive, dry-run reap intent, the unclean-reconcile grace withholding/restart, andwriteFileAtomictemp-cleanup on a rename failure (#771). -
@overeng/genie: Respect repository ignore rules during
.genie.tsdiscovery, so ignored local state such as nested agent worktrees does not become ambient generation input while untracked non-ignored generator sources are still discovered. Checked-out Git submodules are now included in the tracked discovery path as well. -
@overeng/otelite: Honor durability-before-ack — flush each export to the kernel before the 200/OK.
tokio::fs::Filebuffers writes, sowrite_allalone did NOT guarantee the bytes reached the kernel before the sink acked; an independent reader (or a crash) before the next flush could miss them, contradicting R05 ("flush … before acking") and theappend_linedoc's own "durably reaching the kernel before returning" promise. This surfaced as a CI flake in thedurable_before_ackgate (a read immediately after the 200 occasionally saw an empty file under thread contention — reproduced ~1/60 at 16 test threads). Fix:SignalFile::append_line/append_jsonnowflush()afterwrite_all, before returning. This is a flush, not an fsync —sync_all(physical-disk durability) stays deferred to shutdown, so the M2 "no per-export fsync under the lock" throughput decision is preserved. Verified: 0 failures over 200 × 16-thread runs (was ~1/60). -
@overeng/otelite: Make the HTTP-JSON metrics receive path lossless, fixing two silent data-loss bugs a stress test surfaced. The upstream
opentelemetry-protowith-serdedeserialize — which the receiver used to BUILD the proto value the sink then re-serialized — silently drops several metric JSON shapes: asum/gaugeNumberDataPointwhose int64 value is the default string form ("asInt":"7") lost its value entirely (captured null), and a regularhistogrammetric was dropped down to{name,description,unit,metadata}(its data oneof gone). Both returned HTTP 200 + bumpedcounts.metrics→ a silent mis-capture that violates the lossless + "loud, never silent" contracts (decisions/0011). Fix: on the JSON metrics path,with-serdestill runs purely as the dialect VALIDATOR (Err → 400 +note_rejected, gate unchanged), but on success the receiver now persists the VALIDATED RAW JSON body verbatim (re-emitted throughserde_json::Valuevia the newSink::write_metrics_json, counting metrics from the JSON structure) instead of the lossy proto re-serialization. Since the body is already canonical OTLP/JSON andinspectwalks raw JSON, the JSON metrics path is now lossless for string-int64 sums/gauges, regular histograms, AND exponential histograms — the last also RESOLVING the previously-documented exp-histogram-on-JSON limitation for the receive path. Traces/logs JSON paths and all protobuf/gRPC paths are unchanged (already lossless). New gates (real receiver, no mocks): an HTTP-JSON round-trip of a string-int64 sum + histogram + exponential histogram all survive receive → capture →inspect; cross-transport equivalence extended to metrics (the same logical string-int64-sum + histogram over HTTP-JSON vs HTTP-protobuf vs gRPC flattens to equivalentinspectrows, the proto/gRPC fixture built natively to avoid the lossywith-serdesource); and a loud-rejection guard that a malformed metrics JSON body still 400s + is captured nowhere. KNOWN RESIDUAL: the upstream metricswith-serdeis more lenient than the trace one, so for metrics the JSON dialect gate is effectively structural (malformed JSON / hard field-type mismatches), tolerating some non-default dialect shapes (numeric int64 nanos, string enums) rather than rejecting them loudly — a stricter metrics dialect gate is a follow-up (#769, #772).
-
@overeng/notion-effect-client / @overeng/notion-md: Add optional
property_descriptorsfield toNmdFrontmatterV2— compact, non-authoritative property identity hints (property_id, property_type, data_source_id, config_hash) embedded in.nmdfrontmatter for datasource page files. Decoded strictly (unknown fields rejected, R13); field is absent for standalone pages and omitted from encoded output when not set (R10).buildFrontmatterV2insync.tsgates descriptor emission on datasource parent presence. Standalone validity enforced: a descriptor-bearing.nmdround-trips via the standardnotion-mdparse path (R03).docs/file-format.mdupdated with a Property Descriptors section. -
@overeng/megarepo:
mr store gcnow reclaims cold named-branch (refs/heads/*) worktrees, the dominant store accumulation that default gc previously protected unconditionally. A named worktree is reclaimed only after passing layered, short-circuiting gates: (1) a hard cross-megarepo live-set veto (collectStoreLiveSet, store-wide — arepos/symlink alone never protects; only a recordedlivePathsentry does); (2) staleness — the branch's GitHub PR is merged or closed (an open PR, no PR, or anygh/resolver failure ⇒ keep); (3) a lossless floor — every local commit is reachable on a remote (git rev-list <head> --not --remotesempty afterfetch --prune), no stash, no unpushed commits, and a per-repo fetch failure keeps all of that repo's worktrees; (4) three grace timers tracked against a persisted observation ledger — absence grace (default 14d) for every cold worktree, plus a post-merge grace (default 7d aftermergedAt) for merged branches (closed-unmerged has no post-close grace). Capture is two-phase: a qualifying worktree isgit worktree moved to<repo>/.archive/<branch>--<ISO8601>/and its local ref freed (somr applyre-materializes it), then reaped (hard-deleted) once it ages past the archive retention TTL (default 30d); gc also reaps pre-existing.archive/worktrees it formerly ignored. Timers are overridable via$STORE/.state/gc-config.json. Before any deletion gc reconciles ALL registered workspaces (not just the current one), andmr apply/sync/pull/pinnow refresh the liveness record — closing a bug where a repinned-but-unre-registered workspace's live worktree could be deleted; reconcile-all fails safe (a present-but-unreadable workspace keeps its last-known live paths, and grace is not advanced for an unclean reconcile). Archive and reap both re-check the live-set veto under the worktree lock immediately before acting. Provably lossless and conservative: absence of evidence never licenses deletion.mr store gc --jsonresults gainarchived/reaped/keptstatuses with a stablereasontag and (forarchived) arecoverPath.--allis unchanged (nuclear, live-set-bypassing). Design indocs/decisions/0001–0006, terms indocs/glossary.md, spec indocs/spec.md(#771). -
@overeng/otel-contract: Add the schema-first OTEL operation and metric-contract DSL (
OtelOperation,OtelMetric,OtelSpan.withStream, attribute builders, compiled metadata,encodeSync, and checked dynamic span-map annotations) and migrate product instrumentation across the repo off rawEffect.withSpan/Stream.withSpan/Effect.annotateCurrentSpanand normalunsafe*contract calls. The contract remains runtime-light: it owns schema-backed names, labels, attributes, cardinality metadata, and encoders, while package-local code keeps exporter/provider setup, service identity, Restate replay gates, and runtime-specific bridges.@overeng/oxc-confignow shipsovereng/no-raw-otel-primitiveswith generated rollout config,@overeng/utils-dev/otelitegains reusable metric/log expectation helpers, andrestate-effectadopts the same idiom for internal spans while preserving hook-owned Restate spans and replay-aware metrics. -
@overeng/utils/node/otel-attrs: Add schema-first OTEL attribute and span contracts (
OtelAttr,OtelAttrs,OtelSpan) plus otelite expectation helpers that derive span assertions from the same compiled attribute encoders used by runtime instrumentation. Ambiguous encodings fail closed unless explicitly annotated, redacted values only support redacted/drop policies, and span definitions require the dedicatedOtelAttr.spanLabel()contract. -
@overeng/utils-dev/otelite: Add an Effect-native otelite test harness and trace assertion DSL.
OteliteTestHarnesswraps scopedOtelite.capturelifetimes, in-process OTLP exporter wiring, serialized env-backed capture setup, flush-before-inspect helpers, and ergonomiccaptureTest/captureInProcessTrace/captureEnvTracehelpers.expectTraceadds runner-agnostic span assertions for service/name/attribute matching,span.labelenforcement, and same-trace topology checks. -
@overeng/utils-dev/otelite: Add a vitest ↔ otelite capture bridge that wires an in-process
Otelite.capturereceiver to a vitest test's OTLP trace exporter, so spans emitted IN-PROCESS through the normal@effect/opentelemetryOtlpTracerlayer land in a capture the test can assert over.makeOteliteCaptureLayer(options?)is a scopedLayerthat boots ONE receiver, exposes itsCaptureHandlevia the newOteliteCaptureContext.Tag, AND installs the trace exporter pointed at${handle.endpoints.http}/v1/traces; used with@effect/vitest'slayer(...)it gives a PER-FILE lifecycle (one receiver per test file, shared across that file's tests; tests disambiguate by a uniqueservice.name/ span name) — the cheap default per decision 0015, with per-test available by giving eachlayer(...)its own instance. A test doesconst cap = yield* OteliteCapture; …; yield* cap.inspect({ signal: 'traces', name }).flushCaptureSpans()force-flushes the exporter (the emitter's job) before inspecting. Silent-failure guard: a misrouted exporter (the/v1/tracessuffix bug, see Fixed) lands nothing, so the demonstrator's non-zeroinspect/span_countassertions FAIL the test rather than pass vacuously. Real-binary tests emit a span in-process through the REAL exporter and assert it round-trips, plus a regression that a bare un-suffixed URL captures nothing (#769, #772). -
@overeng/notion-effect-client: Add a real-consumer span-assertion demonstrator (D3, decision 0015) co-located in this client's own test suite (
src/test/otelite-span-shape.test.ts). It drives the REAL instrumented query path (NotionDatabases.query→executeRequest→Effect.withSpan('NotionHttp.POST')) against a STUB upstream — aHttpClient.make(...)answering the onePOST /data_sources/{id}/queryendpoint with a canned empty paginated list +x-ratelimit-remaining/x-request-idheaders — under the@overeng/utils-dev/otelitecapture bridge, with NO secrets and NO network. It asserts the emitted span shape: exactly oneNotionHttp.POSTspan carrying the templatednotion.http.route=/data_sources/{data_source_id}/query+notion.http.method/operation/status_code(200) +notion.rate_limit.remaining(42); exactly one autohttp.client POSTchild from@effect/platformwhoseurl.pathproves the stub served the request; a non-zerospan_count(silent-export guard); and a public-repo leak guard that NO captured span attribute carries anauthorizationheader or the token value (@effect/platformrecords only a header subset and excludes Authorization). The churn-couplednotion.http.*assertions sit next to the instrumentation that churns; the bridge stays a lean shared helper. The shadowing gotcha (the bridge re-exports the exporter'sFetchHttpClientasHttpClient.HttpClient) is resolved by providing the stub to the effect-under-test directly (Effect.provide, innermost-wins) so the consumer sees the stub. Runs the real nix-builtotelitebinary onPATH(#769, #772). -
@overeng/utils-dev/otelite: Add a scoped in-process
Otelite.captureprimitive for harnesses that own the system-under-test lifecycle themselves (vsrun, which spawns the child).capture(options?)spawnsotelite capturewith piped stdin/stdout and yields a scopedCaptureHandle(endpoints,outDir,inspect,summary). It learns the ephemeral receiver endpoints by decoding the FIRST stdout line against a newEndpointsEvent/otelite.endpoints/v1Schema— dispatching on theschematag, never string-scraping. Closing the scope stops the receiver by closing the child's stdin (EOF), drains in-flight exports, and resolveshandle.summary(the finalotelite.summary/v1line); teardown is interrupt-safe so an interrupted scope leaves no orphaned child. The handle'sinspectpinssrcto the out-dir and does a small bounded short-poll retry on a transient 0-row read (the receiver writes each export straight to the file withwrite_allbefore acking, so a captured span is durable immediately — but an independent reader can briefly observe 0 rows from pure scheduler/fs-visibility latency). Real-binary tests raw-POST a known OTLP/JSON span and assert the typedSpanRowround-trips throughinspectandsummary.counts.spans(#769, #772). -
@overeng/notion-datasource-sync: Replace legacy body hash pointers with typed
BodyIdentity/BodyProjectionPayloadsemantics so remote body evidence, rendered content digests, safety, materialization, and OTel body attributes have explicit ownership boundaries and cleaner replay/testing contracts (#766). -
@overeng/content-address + Notion body sync: Add reusable content-address primitives (
ContentDigest,ContentDescriptor, canonical JSON hashing, descriptor verification) and use Notion body observation evidence fingerprints for guarded body planning and settlement. -
@overeng/otelite: Incubate a coordination-free local OTLP capture tool for E2E and instrumentation tests — effect-utils' first Rust package. Native receiver (HTTP json+proto + gRPC) runs a child with
OTEL_*env, captures canonical OTLP/JSON, and feedsinspectfor assertions; a typed Effect wrapper sits on top. M1a lands the Nix build lane (nix build .#otelite,nix run .#otelite). VRS incontext/otelite/; epic in #772 (#769). -
@overeng/otelite-effect: Add a thin, Effect-native wrapper package around the
oteliteCLI (M9). Shells out via@effect/platformCommand(nonode:child_process), decodes the CLI's sevenotelite.<name>/v1JSON schemas withSchema, and exposes anOteliteEffect.Servicewithrun(scoped capture of a child) andinspect(typed rows or summary per signal). otelite'ssysexits.hexit-code taxonomy maps onto tagged errors (OteliteSpawnError,OteliteChildFailed,OteliteCliError,OteliteDecodeError). The CLI JSON output is the single source of truth — the wrapper never reimplements capture/inspect. Tests run the real nix-built binary (#769, #772). Adds a real end-to-end test (D1): a hand-instrumented Effect emitter (a@effect/platformHttpClientPOST of a known OTLP/JSON span, no OTel SDK / no new deps) runs as the child underOtelite.run, and the span round-trips back out through the typedinspect(SpanRow+TraceSummary,Schema-validated) — closing the wrapper↔binary loop on the wire. -
@overeng/otelite: Close the test-coverage gaps the stress-test pass surfaced, and generalize the example goldens into laws. (A) Targeted end-to-end gates for paths only unit-tested before: gRPC now drives metrics AND logs (not just traces) through the receiver; an exponential histogram round-trips the protobuf receive path → capture →
inspect --signal metrics(the JSON path drops it, an upstream gap); and a SIGINT mid-run(whole-process-group Ctrl-C) is a regression guard proving otelite swallows its own signal, lets the child take it, drains, and still emits the summary reporting128+SIGINT. (B) Property-based invariants (proptest, dev-only) for the two derivation paths whose one real bug (the OK-span error miscount) motivated them:derive_spanmetrics(callspartition the spans; ERROR subset = status code 2; onedurationper dimension; buckets sum to count; min≤mean≤max) andinspect::summarize(span/error/zero-duration counts match the population regardless of order/grouping). 43 crate tests green;nix build .#otelitedoCheckruns them in-sandbox (#769, #772). -
@overeng/notion-core + @overeng/notion-effect-client: Add shared Notion body-fidelity vocabulary and live Markdown/block-tree observation so downstream packages can distinguish complete remote bodies from lossy endpoint output.
-
@overeng/restate-effect: Docs-refinement pass + the HTTP error-classification consumer recipe. (1) A new verified example
examples/14-http-error-classification.ts(+src/error/http-error-classification.integration.test.ts) shows anHttpClientcall classifying real HTTP outcomes into the typed error channel — 400/403/404 → terminal domain errors, a malformed 200 body → a terminalMalformedUpstream, 429/5xx/timeout → theRestate.retryableUpstreamUnavailablewith the 429'sRetry-Afterprojected — across BOTH transient-retry strategies (Restate's durable STEP retry vs. a caller-visiblebacking-offhandler retry), making explicit the journal footgun that a transient outcome must NOT be committed to aRestate.runstep (a replay re-serves the stale transient instead of re-fetching). Driven end-to-end against a nativerestate-servervia a tiny in-process upstream (8 assertions). A guide section indocs/guide/schema-and-errors.mdteaches the recipe; the spec mention is indocs/vrs/04-error-boundary/spec.md. (2) An end-to-end idempotency recipe indocs/guide/durable-steps.mdthreads ONE producer identity (intent-id → Virtual-Object key → Workflow id → send dedupe key) throughRestate.idempotencyKey, with the different-key-per-layer misuse called out. (3) Docs friction fixes: therunDescriptorthunk-vs-run-Effect distinction (determinism/durable-steps), the@effect/platform-nodepeer-dep install step beside the firstserve(getting-started/endpoint), a worked Service+Object-on-one-endpointAppR-union example (endpoint), the durable-promise-key-vs-signal-name clarification (constructs), the named-classretryableretryAfterprojection (annotations). (4) Repointed the 13 guide →src/links left dangling by the subsystem-subdir reorg (e.g.src/cancellation.integration.test.ts→src/runtime/cancellation.integration.test.ts). Docs + one example; no library surface change. -
@overeng/restate-effect: Optional/nullable State (migration-blocker) + two typing papercuts (epic #757). (1) Optional/nullable State — a state field declared
Schema.optional(S)(a nullable cursor, e.g. ahighWatermarkwatermark) is now expressible ANDstateOf-readable end-to-end. Restate State is K/V, so an ABSENT key reads back asundefinedand writingundefinedREMOVES the key:State.set(key, undefined)≡State.clear(key)in handlers, and the teststateOfproxies (RestateTestHarness.stateOf/RestateTestEnv.stateOf, mock AND real) gained the sameset(undefined)-removes semantics plus aclear(key)method.State.forstores the optional field's inner present-value schema for serde (normalizeStateSchemastripsundefined), so a write only ever encodes a presentTand a read of an absent key returnsundefinedwithout hitting the serde — ONE pattern that type-checks under BOTHtsc(exactOptionalPropertyTypes) and the bundler (a bare top-levelSchema.UndefinedOrhandler RETURN is not, sinceJSONSchema.makerejects it; a nullable value belongs in State or a struct field).AnyImplementation/materializeObject/materializeWorkflowwidened fromRecord<string, Schema>toStateSchemasso an optional-state Object/Workflow materializes. (2)domainStateacceptsSchema.optional—RestateScheduled.make(Restate.pollLoop)domainStateis nowStateSchemas(shared State-schema handling), so a poll-loop cursor can be nullable. (3) Typed cycle error channel without a cast —CycleEffect/ScheduledConfig/makegained aCycleEtype param tied toerrorSchema's decoded type, so the cycle's error channel isRestateError | CycleEand a typed cycleEffect.fails its declared error and composes WITHOUT the prioras unknown ascast (the composed-daemon example's cast is removed; the loop'srunCycleabsorbs the declaredEthroughclassifyOutcome). (4) Safe-by-default span projection —Restate.annotateSpanFrom(schema, value, pick?)projects a decoded struct to span attributes and STRIPS everyRestate.sensitive/redactedfield (even if explicitlypicked), using the samefindSensitiveFieldswalk the serde redaction uses — closing the leak path the free-formRestate.annotateSpan(raw primitives, can't detect sensitivity) otherwise left open, so a redacted value can never reach a span by accident. Covered server-free (in-memoryTestContextnullable State set/get/clear via the real combinators;RestateTestEnvmock backend;annotateSpanFromstrips sensitive incl. when picked) AND against a nativerestate-server(RestateTestEnv.realnullable State end-to-end throughstateOf+ handler; an optionaldomainStatecursor advances then clears in a pollLoop). VRS: 01-authoring (optional/nullable State), 02-schema-serde (State value normalization), 06-scheduling (domainStateoptional + typed cycleE), 08-observability (annotateSpanFromredaction rule);docs/guideconstructs/observability/annotations updated. No new dependencies; all 140 package tests green. -
@overeng/utils: Shared SSOT helpers used by
@overeng/restate-effect's Core S cleanups (additive, dependency-free). (1)@overeng/utils/node's newnet.tsexportsfreePort()(the single "ask the OS for a free TCP port" helper, previously hand-copied 4× across the playwright config factory'sfindAvailablePortand the restate-effect test harness — all of which shared the same TOCTOU race), plusfreePorts(count)(allocate N DISTINCT ports as one batch — holds every:0listener open until all are read, so the OS cannot hand the same port to two of them, unlikePromise.all([freePort()×N])) andwithFreePort(fn, { retries? })(run a bind-by-number consumer against a fresh port, RETRYING onEADDRINUSE— the TOCTOU-closing path for a child process that cannot be handed an already-listening socket). (2)@overeng/utils(isomorphic)formatReasonMessage({ reason, label?, method?, cause? })is the SSOT for the tagged-errorget message()body our errors hand-copy (space-joinreason+ optional[label]+(method)+: <cause.message>), preserving the existingRestateError/PtyErroroutput verbatim. Covered bysrc/node/net.unit.test.ts(6) +src/isomorphic/string.unit.test.ts(5). -
@overeng/restate-effect: Code-reuse / SSOT cleanups + the
freePortTOCTOU fix (Core S). (1) Deleted the now-deadtest/restate-server.ts(subsumed by./testing's productizedstartServer) and the unusedtest/test-utils.ts— both were imported by nothing (~190 lines removed). (2) The four hand-copiedfreePortport-0 helpers (testing.ts, the two gone test files, and@overeng/utils's playwrightfindAvailablePort) — plus a 5th inscheduled-durability.integration.test.ts— now all consume the single@overeng/utils/nodefreePort/freePorts. The shared TOCTOU is fixed where it bites: the nativerestate-serverboot allocates its 3 listener ports via the collision-freefreePorts(3)batch and the whole boot is RETRIED on a port collision (detected via anaddress in use/EADDRINUSEsignature in the server's early-exit logs) with a fresh batch — so a co-tenant grabbing a port in the bind-release gap (theAddress in useparallel-boot flake) just retries instead of redding the lane. Verified by running the integration lane under 8–12 parallel server boots × 8 repeats with zero collision failures. (3)RestateError.messagenow delegates to@overeng/utils'sformatReasonMessage(behavior-preserving). (4)Serde.ts's inlinenew TextEncoder()byte encoding is replaced with@overeng/utils'stextEncodeToArrayBuffer(the byte-encoding SSOT).@overeng/utilsis now a peer + dev workspace dep of@overeng/restate-effect(mirroringnotion-effect-client), so utils's peer surface propagates to the consumer rather than bloating this dependency-light core. No behavior change; all 134 package tests green. -
@overeng/restate-effect: Admin / management API (
./admin) + Molty operating-a-deployment runbook + workflow-id/idempotency-key span stamping (Core M, decision 0018, spec §16). A new OPT-IN./adminsubpath (dependency-light, like./otel/./testing) exports aRestateAdminTag +layer({ adminUrl, apiKey? })/layerConfig()MIRRORING theRestateIngresspattern (decision 0016) but bound to the ADMIN url — every operation an Effect failing withRestateError({ reason: 'AdminFailed' }). Operations map 1:1 onto the restate-server admin REST API (verified against 1.6.2, admin-api-version 3): invocationscancel/kill/pause/resume/purge/purgeJournal/delete(PATCH|DELETE /invocations/{id}[/{verb}]) +restartAsNew({ from?, deployment? })(restart-from-journal-prefix); deploymentsregisterDeployment/listDeployments/getDeployment/updateDeployment(POST|GET|PATCH /deployments[/{id}]); introspectionquery(sql, rowSchema)(a THIN TYPED PASSTHROUGH — the caller supplies the SQL AND the row Schema since the binding does not own thesys_*shapes; a decode mismatch →AdminFailed) /queryRaw(sql)overPOST /query. The raw HTTP lives in ONE bare-client module (AdminApi.ts) that BOTH./adminand the harness (./testing'sstateOf+ deployment registration) consume — lifting the harness's previously-duplicated fetch-against-admin code so the two never drift. Trust boundary documented prominently: the admin API is unauthenticated by default (never expose publicly; bearerRedactedapiKeyfor a secured/Cloud endpoint) and less stable than the SDK protocol (pinned to 1.6.2). Observability (#5): the boundary now ALSO auto-stampsrestate.workflow.id(the Workflow key) andrestate.idempotency.key(the original-invocationidempotency-keyheader) on the attempt span — so a consumer slices on the end-to-end identity without hand-rolling them; both are identity values, never a redacted FIELD (a smallBoundaryObserverseam change inEndpoint.ts+ the./otelstamper). Verified server-free (src/admin.test.ts: per-op method/path/auth + typed-query decode-failure;src/observability.test.ts: the two new attrs via an in-memorySpanExporter) and against a real native server (src/admin.integration.test.ts: list deployments + a typed/queryround-trip, QUERY an incident object's State, and surface + cancel a wedged delivery). The Molty runbook recipe isexamples/13-admin-operations.ts(anincidentVirtual Object + adeliveryWorkflow that wedges); the guide page isdocs/guide/admin-operations.md. Version caveats found against 1.6.2: the BULK/BATCH invocation verbs do NOT exist (they 405 — a later-server feature), and a Workflowrunblocked on a long durable wait reportsstatus = 'running'(notsuspended), so "stuck delivery" is non-terminal-ranked-by-retries. "Admin / management wrappers" is removed from the spec's Deferred list. No new dependencies. -
@overeng/restate-effect: Swappable mock⟷real
RestateTestEnvfaçade + the eight real-server e2e coverage gaps (decision 0017, spec §11)../testingnow exportsRestateTestEnv: ONEContext.Tagwhose surface is the CONTRACT-ADDRESSED invocation level (invokeService(contract, method, input)/invokeObject/submitWorkflow/signalWorkflow/attachWorkflow/stateOf/resolveAwakeable/kind), with TWO Layer impls satisfying the same Tag —RestateTestEnv.mock({ services, appLayer })(in-process, no journal, no server, ms) andRestateTestEnv.real({ services, appLayer, alwaysReplay?, disableRetries? })(a thin wrapper overRestateTestHarness) — so the SAME test body (authored ONLY againstRestateTestEnv) runs on either backend viait.each(['mock', 'real']). The load-bearing property:invoke*carriesRestateError | ErrorOf(the TYPED declared error) on BOTH backends, socatchTag(DomainError)compiles AND recovers identically — the mock recovers the typedEby round-tripping the failure through the contract'serrorschema (the SAME decode an ingress caller performs on a terminal body). This also made the bound harnessingress.callTyped/objectCallTypedtyped form the default invoke (the precise typed-error union no longer widens; the old escape to the standalonecallTypedis gone). The mock reuses the package's real building blocks (NOT a re-implementation): the capturedRuntime<AppR>, the in-memorymakeTestContext(extended with a shared awakeable registry so aresolveAwakeablefrom OUTSIDE a handler completes a suspended one), the new sharedEndpoint.provideHandlerCapsper-kind marker provision (the single source of truth, now also used by the realmaterialize*boundary — they cannot drift), the journaleddeterminismLayer, and the boundary'sclassifyOutcome; per-key StateMaps give object/workflow key isolation for free. The lower-levelRestateTestHarness+makeTestContextLayerprimitives stay available (additive).RestateTestHarnessgainsregisterDeployment({ services, appLayer })(serve + register a SECOND endpoint VERSION on a fresh ephemeral port — multi-deployment upgrade) and endpoint observability wiring onlayer(hooks/inboundBridge/boundaryObserver). Fills the eight previously-unproven real-server e2e gaps with native-server integration tests: (1) in-handler service→serviceRestate.call/Restate.send(cross-invocation); (2) deterministic durable concurrencyRestate.all/race(source-order tuple despite resolution order); (3) multi-deployment version upgrade (two deployments coexist, a new invocation routes to the latest — the full suspend-straddle-upgrade cross-version replay is a documented follow-up); (4)runExitsaga-compensation (a failed durable step observed as anExitdefect → a compensating step); (5)disableRetriesfail-fast vs retry as an assertion + theRestate.retryable+retryAfterprojection driving retries; (6)sensitive-field redaction ON THE WIRE (a raw ingress response holds ciphertext for the field + decrypts back; a missing cipher fails loudly, never plaintext); (7) OTel exactly-once per-invocation metrics + attempt-span reparenting under REALalwaysReplay; (8)DurablePromise.peek(non-blocking durable-promise read). A parametrizedsrc/test-env.integration.test.tsproves the same body on both backends; the eight gaps live in dedicated*.integration.test.ts. VRS: decision 0017, spec §11.6 + the §11.3 layering table (the mock backend is the unit row promoted to a contract-addressed surface),docs/guide/testing.md(theRestateTestEnvsection + mock-vs-real matrix + a consolidatedmakeTestContextLayeroptions table), and the glossary. No new dependencies. -
@overeng/restate-effect: Effect-idiom fixes — replay-aware logging, secured ingress auth, request identity, property-based serde (decisions 0015, 0016; R37–R39). (1) Logger →
ctx.consolebridge (decision 0015): a per-invocationloggerLayer(ctx)is now provided over every handler effect ALONGSIDE the determinism layer, replacing Effect's default logger so an in-handlerEffect.log*writes to the invocation's replay-awarectx.console— suppressed during replay (no more re-emitting the same line on every replay/attempt, the bug aglobalThis.console-backed logger has), level-controlled viaRESTATE_LOGGING, and stamped with invocation context. The line is formatted by Effect's ownlogfmtLogger(soEffect.annotateLogs/spans ride along); only the sink changes. On the CORE.export (no./otel). The endpoint's own startup log is outside a handler and unaffected. (2) Secured ingress auth (decision 0016, R38):RestateIngress.layerkeeps the literal{ url }primitive and gainsapiKey?: Redacted<string>(+ extraheaders), sent asAuthorization: Bearer …so a SECURED / Restate Cloud ingress is reachable (impossible before); the key is aRedactedso it never prints.RestateIngress.layerConfig()is theConfig-then-literal wrapper readingRESTATE_INGRESS_URL+ an optionalConfig.redacted('RESTATE_INGRESS_KEY'). (3) Request identity (decision 0016, R39):EndpointOptions.identityKeys?: ReadonlyArray<string>(ED25519 v1 public keys) threads intocreateEndpointHandler({ identityKeys })→ the SDK'swithIdentityV1, so the SDK rejects unsigned/unauthorized inbound requests — closing the unauthenticated handler-endpoint hole (pure passthrough).EndpointOptions.portnow also acceptsnumber | Config<number>(resolved on layer acquisition;layer/serve's channel becomesRestateError | ConfigError), andRestateOtel.layerConfigreadsOTEL_SERVICE_NAME/OTEL_EXPORTER_OTLP_ENDPOINTand hands the resolved endpoint to a caller-supplied exporterbuild(the OTLP exporter package stays the consumer's choice — not in the closure). (4) Property-based serde round-trips (spec §11.4, now REAL):@effect/vitestit.propderives afast-checkArbitraryper schema and assertsdeserialize(serialize(x))equivalent toxviaSchema.equivalence(nottoStrictEqual) for a plain struct, a transformed schema, an optional state field (normalizeStateSchema), and the redaction transform (encrypt(decrypt(x)) ≡ xby value, fresh IV per encrypt) — closing the previously-false §11.4 "first-class" claim; JSON-unrepresentableNaN/±Infinityare excluded viaSchema.Finite(not round-trippable by design). A parity guard asserts the journaledRandomoverrides every generator method of the defaultRandom(catching a future silent determinism hole). The BLOCKINGdocs/guide/observability.mdsnippet that imported the undeclared@opentelemetry/exporter-metrics-otlp-httpis fixed to matchexamples/09-otel.ts(PeriodicExportingMetricReader+InMemoryMetricExporterfrom@opentelemetry/sdk-metrics), and a LOGGING section documents the new bridge. All covered server-free (src/Runtime.test.ts,src/Serde.test.ts,src/identity.test.ts,src/client-ingress.test.ts,src/otel.test.ts). No new dependencies. -
@overeng/restate-effect: Composed
pollLoop— Retry-After re-arm + webhook wake (decision 0012).RestateScheduled.make(Restate.pollLoop) now composes two opt-in behaviors.errorSchemadeclares the cycle's error union (annotatedRestate.retryable/Restate.terminal) and routes a cycle failure through the boundary'sclassifyOutcome(the single source of truth): aretryablemember RE-ARMS the next cycle after its projectedretryAfterfloor (read off the failing instance, e.g. a 429'sretryAfterMillis) with the cursor AND iteration FROZEN — the same logical cycle retries, not an advance — while aterminalmember / defect falls toonCycleError;maxRetryBackoffs(default unbounded) caps consecutive backoffs before demoting to the policy. In the default no-wake shape the backoff is a delayed self-send (generation-bumped, so the pre-armedfixedDelaysend no-ops), so the per-key write lock is RELEASED during the backoff andstopmid-backoff stays prompt (measured ~3ms into a 3000ms backoff).wake: trueopts the inter-cycle wait intoRestate.race([sleepDescriptor(delay), wake.descriptor]): each cycle opens a fresh awakeable, persists its id (wakeId, a SHARED read-only handler so a webhook can read it under the held lock) and threads the wake reason to the next cycle aswokenBy; an ingressresolveAwakeablecuts the wait short and the next cycle fires with delay 0. The id ROTATES per cycle; a stale id resolves harmlessly. Documented tradeoff: wake mode HOLDS the write lock during the wait (exclusivestop/startqueue behind it, bounded by the sleep leg) — pair wake with shortretryAfterfloors; the no-wake shape is wedge-free. The two shapes are materialized as two distinctcyclebodies. This also fixes a general boundary-correctness bug:classifyOutcomenow reads theterminal/retryableannotation PER UNION MEMBER (resolving the matching member for the actual failing error) rather than off the un-annotatedSchema.Unionnode — without this every retryable union member silently mis-classified as terminal. Covered server-free (union-member classification inerror-transport.test.ts) and against a nativerestate-server(scheduled-compose.integration.test.ts: Retry-After re-arm + no-wedge stop mid-backoff + terminal-member skip + wake early-fire/rotation/stale-id-harmless;scheduled-durability.integration.test.ts: SIGKILL mid inter-cycle wait resumes after restart). The verified composednotion-watch-style daemon lives inexamples/12-self-reschedule.ts;docs/guide/scheduling.mdhas the worked example. -
@overeng/restate-effect: In-memory
TestContext+ harness ergonomics + durability lint (#5, decision 0013)../testingnow exports a FAITHFUL in-memoryRestateContext(makeTestContext/makeTestContextLayer) for SERVER-FREE unit tests of handler LOGIC + State transitions — a real in-memory implementation, NOT a stub: State is a realMapround-tripped through the sameeffectSerdethe handler uses,ctx.run(name, …)executes once and MEMOIZES by name (journaled-once: a re-runreturns the stored value),ctx.date/ctx.randare deterministic (seeded),ctx.sleepis a controllable no-op, and the layer provides the SAME capability-marker subset the real boundary provides perhandlerKind(so aState.setin a read-only handler is still a compile error). Provide it over the real handler effect and assert on the result AND the StateMap. It deliberately does NOT model durability/replay, single-writer/per-key concurrency, or cross-handler/cross-invocation effects (call/send/reschedule/delayed self-send/pollLoop/cross-invocation durable promises) — documented (JSDoc + README + decision 0013 + spec §11.5) as NOT a substitute forRestateTestHarness(the real native server).withRestateServer({ services, appLayer })collapses the copy-pasted ~25-linebeforeAll/afterAllscope/ingress boilerplate intosetup/teardown+ aharness()accessor; the six endpoint-based integration tests (awakeable, cancellation, object, end-to-end, workflow) plus the self-reschedule suite are migrated onto it.liveSleep/withLiveClocktest utils pin anEffect.sleep/ sub-program to a liveClockso wall-clock waits coordinating with the native server elapse under@effect/vitest's virtualTestClock. Theovereng/no-non-durable-waitoxlint rule is enabled on handlersrc/(alongsideno-raw-nondeterminism), exempting test + harness/testing infra files. Determinism-hazard claims verified at the type level (capability-inference.types.ts): a nested journaled op insideRestate.runis already a COMPILE error via the run-scrubbing (no new rule needed); gating aRestate.runon a journaledState.getis deterministic (a non-hazard, left legal). Covered bysrc/TestContext.test.ts(server-free handler/State unit tests against the real combinators). -
@overeng/restate-effect: Self-reschedule — durable daemons as a chain of delayed self-sends (#4, decision 0012).
Restate.reschedule({ contract, method, input, delayMillis })is the typed durable self-send building block: a keyed handler re-arms one of its own handlers via a delayed one-way send (readsRestate.key; capability-gated to keyed handlers viaObjectKey; journaled → idempotent under replay).RestateScheduled.make(aliasRestate.pollLoop) is a narrow durable recurring-loop primitive: it materializes a Virtual Object that runs ONE boundedcycleof the user's work then re-arms via a delayed self-send, so each invocation has a bounded journal (does not grow with cycle count) and crash/restart durability is free (the pending timer survives a restart). ShipsfixedDelayscheduling,onCycleError(defaultskipToNext; alsostopLoop), stop viastopWhen/maxIterations/in-cycle{ stop: true }, astart/stop/statuscontrol surface (statusis a shared read-only query; the internalcycleisingressPrivate), a generation token that invalidates stale delayed timers without a timer handle, and the SAFE re-arm-before-fallible-work ordering (a re-arm journaled before a failure is still delivered, so the loop survives a failing cycle). There is intentionally NOretryCycleknob — per-cycle durable retry belongs inside a BOUNDEDRestate.run(Restate journals a give-up a primitive cannot honestly re-run, and an unboundedRestate.runwedges the per-key write lock sostart/stopblock).fixedRate/cron/runtime reconfigure are deferred. The README +examples/12-self-reschedule.tsmake the p99 latency teaching prominent (a durable daemon uses a one-way send + delayed self-send, never a blockingcall— measured 18.4s p99). Covered bysrc/scheduled.integration.test.tsagainst a nativerestate-server: basic recurrence + exactly-once,maxIterations/data-driven stop, stop→restart, generation idempotency (a duplicatestartnever overlaps),skipToNext/stopLooppolicies, thereschedulebuilding block, and the README example end-to-end. -
@overeng/restate-effect: Fix
makeJournaledClockdroppingClock.sleep(Runtime). The journaled per-invocationClockwas built via{ ...Clock.make(), … }, butsleep(and the syncunsafeCurrentTime*) live on the Clock PROTOTYPE, not as own-enumerable properties — the object spread silently DROPPEDsleep, so a bare in-handlerEffect.sleepthrewclock.sleep is not a function(it surfaced as a retry loop under load). Rebuilt prototype-preservingly (Object.assign(Object.create(getPrototypeOf(base)), base, overrides)) sosleepand the[ClockTypeId]brand survive while the time reads stay journaled. Regression test added: an in-handlerEffect.sleepnow runs without throwing. Real package defect found under load. -
@overeng/restate-effect: End-user documentation for the stable surface (Phase 6). A README covering the mental model, a first Service end-to-end, the three constructs (Service, Virtual Object + typed State, Workflow + durable promises), Schema I/O + the typed error boundary, determinism (journaled
Clock/Random,Restate.run, explicit durable waits), durable steps/calls/awakeables + idempotency, cancellation/lifecycle, the endpoint/serve, the./otelbridge, the./testingharness, and an API reference for././otel/./testing. Every README snippet is a real compiled-and-run example: the runnable.tsfiles live inexamples/(covered bydt ts:check), andsrc/examples.integration.test.tsdrives the example contracts/impls through the./testingharness against a nativerestate-server(underdt check:all), so a doc snippet that stopped compiling or running fails CI. Grill-in-flux ergonomics (the exactretryAftersyntax, automatic durable-combinator-infra-failure-to-defect, awakeables joiningRestate.race, the self-reschedule helper, a server-free mock context) are left as clearly-markedTODO(refinement)stubs for the refinement pass. Docs-only — no library surface change. -
@overeng/restate-effect: Completes the v1 Schema-annotation set and the retry surfacing.
Restate.retention({ idempotency?, journal?, workflow? })on a contract or handler I/O schema is read atmaterializeand mapped to the SDKidempotencyRetention/journalRetention/workflowRetentionoptions (explicit builderoptionswin); the R35 service/handler option surface (inactivityTimeout/abortTimeout/ingressPrivate/enableLazyState/explicitCancellation) is now wired for stateless Services too (handler-level viaHandlerSpec.options, service-level via the contract's third arg).Restate.sensitive(aliasRestate.redacted) on a struct FIELD is consumed byeffectSerdeas an encrypt-at-encode / decrypt-at-decode TRANSFORM, read ONCE off the pre-transform property signatures (decision 0011): the annotated field is ciphertext on the wire/journal while every other field stays plaintext, round-tripping back to plaintext on decode. The cipher is a pluggable Effect serviceRestateRedaction(Context.Tag, a synchronous{ encrypt, decrypt }byte cipher) resolved once from the captured runtime context atmaterialize;aesGcmRedactionLayer(key)/aesGcmCipher(key)provide an AES-256-GCM reference (random IV per encrypt, self-describingiv‖tag‖ctlayout,node:crypto). A schema with a sensitive field but noRestateRedactionprovided fails with a clearRedactionCipherMissingErrorat encode/decode — never silent plaintext. This is field-level redaction in the serde (the only layer with field structure); the whole-valueJournalValueCodecstays deferred. Retry surfacing (decision 0006): a typedretryPolicy(maxAttempts/initialIntervalMillis/maxIntervalMillis/exponentiationFactor/onMaxAttempts: 'pause'|'kill') and anasTerminalErrorhook on service/handler options map to the SDKRetryPolicy/asTerminalError;Restate.rungains an optionalRunRetryOptions(maxRetryAttempts/maxRetryDuration/intervals/factor) threaded intoctx.run(name, action, options). Durable retries remain Restate's —Effect.retry/Scheduleare for pure logic only (theovereng/no-raw-nondeterminismlint guards). No new dependencies. Covered by server-free unit/contract tests: ciphertext-on-the-wire for the annotated field + plaintext for others + round-trip + missing-cipher failure (XOR and AES ciphers), retention/retry/asTerminalErrormapped onto the materialized SDK definition options, andRunRetryOptionsreachingctx.run. -
@overeng/restate-effect: Docker-free, Effect-native testing harness behind an opt-in
./testingsubpath export (RestateTestHarness).RestateTestHarness.layer({ services, appLayer, alwaysReplay?, disableRetries? })is ONE scopedLayerthat, on acquire, allocates an isolated temp base dir + EPHEMERAL ports for every listener (server ingress/admin/node-to-node AND the SDK endpoint, OS port-0, parallel-safe — R27), spawns the nativerestate-server(binary viaRESTATE_SERVER_BINornix/restate.nix) with the optional determinism env (alwaysReplay→RESTATE_WORKER__INVOKER__INACTIVITY_TIMEOUT=0s;disableRetries→RESTATE_DEFAULT_RETRY_POLICY__MAX_ATTEMPTS=1+..._ON_MAX_ATTEMPTS=kill), polls admin/health+ partition-readiness, serves the consumer's endpoint (theirappLayerthreaded into the served runtime so handlerRis satisfied) on its ephemeral port, and registers the deployment; on release (same scope, reverse order) it closes the endpoint → SIGTERM/SIGKILL the server → removes the base dir, surfacing buffered server logs on any startup failure. The harness exposes a typedingress(theRestateIngresscall surface — Services/Objects/Workflows — pre-bound to the spawned server so tests never threadRestateIngress) andstateOf(contract, key)→ a typedStateProxy(get/getAll/set/setAll, key+value typed against the contract'sstateblock, viaeffectSerdeover the Admin API: JSON-mode/queryhex-decode for reads,POST /services/{name}/statebyte-arraynew_statefor writes) for seeding pre-conditions and asserting post-conditions without going through a handler. No new runtime deps (no Apache Arrow — JSON content negotiation instead). Consumers wire@effect/vitestthemselves. Covered by a consumer-style integration test (Virtual Object + injectedappLayer,stateOfseed/assert round-trip + key isolation, and analwaysReplayreplay-stability run) that gracefully skips when no nativerestate-serveris available. -
@overeng/restate-effect: OpenTelemetry bridge behind an opt-in
./otelsubpath export (RestateOtel).RestateOtel.layer({ resource, exporter | spanProcessor })builds ONE OTelTracerProviderand registers it as the API global AND installs a globalAsyncLocalStorageContextManager(viaprovider.register()) — the load-bearing prerequisite, since@effect/opentelemetry'sNodeSdk.layerregisters neither, leaving the hook'strace.getActiveSpan()undefined and Effect spans orphaned. The same provider is shared with Effect's tracer, soEffect.withSpanand Restate's spans use one provider.RestateOtel.withOtel(endpointOptions)attaches@restatedev/restate-sdk-opentelemetry'sopenTelemetryHookservice-level on every materialized service (the hook owns the replay-awareattempt/runspans + inbound W3C extraction) and wires the per-invocation inbound bridge: at handler entry it reads the active attempt span and reparents the Effect program under it (Tracer.withSpanContext), so caller →ingress_invoke→invoke→attempt→ Effect spans form one coherent trace. Exactly-once-on-replay telemetry is steered throughRestate.run(the load-bearing seam); anisReplayingaccessor is also exposed but documented as version-fragile (it reads the SDK's internalisProcessing). The coreEndpointOptionsgains dep-lighthooks?/inboundBridge?seams (restate types + a pure transform — no otel dep in the core.export). The otel packages are scoped to the./otelsubpath as peers; the@opentelemetry/sdk-*catalog pins are bumped 2.2.0 → 2.7.1 to satisfy@restatedev/restate-sdk-opentelemetry's@opentelemetry/core >= 2.6.0peer. Covered by a server-free contract test (in-memorySpanExporter) asserting the one-trace parent linkage and exactly-oncerunspans / suppressed replay events. -
@overeng/restate-effect: Initial POC of an Effect-idiomatic wrapper around the Restate TypeScript SDK (
@restatedev/restate-sdk1.14.5). ProvideseffectSerde(EffectSchema↔ RestateSerde, with malformed payloads mapped to a non-retryableTerminalError(400)), a per-invocationRestateContextContext.Tagwith durablerun/sleepcombinators, declarative Schema-typed service authoring (RestateService.make/handler), and the endpoint as a scoped (graceful-shutdown)Layerplus aserveentrypoint. DomainSchema.TaggedErrors map to RestateTerminalErrors (no retry,_tagmetadata) while defects propagate for SDK retry. Covered by a Docker-free integration test against a nativerestate-server. Scope: stateless services only — Virtual Objects, Workflows, awakeables, sagas, and the full OTel trace-context bridge are out of scope. -
@overeng/restate-effect: Determinism layer + cancellation↔interruption. Each handler invocation now runs under a journaled Effect
Clock(currentTimeMillis/currentTimeNanos←ctx.date; the syncunsafeCurrentTime*reads ← a per-attempt frozen base seeded once fromctx.date.now()at entry, so they are replay-stable and do not advance mid-attempt) andRandom(←ctx.rand), so default Effect time/random reads are correct-by-construction under replay; durable waits stay the explicitRestate.sleep/timeout/racecombinators (noClock.sleepremap). Theovereng/no-raw-nondeterminismoxlint rule is enabled on source handler code (src/, tests exempt). A Restate cancellation now surfaces as an Effect interruption at the next durable await point:acquireRelease/onInterruptfinalizers and compensations run, the interruption maps to aCancelledError(terminal, not retried) rather than a retried defect, andRequest.attemptCompletedSignalis bridged to attempt-scoped finalization. AddsRestate.cancel/Restate.onCancellation. Fixes a latent issue where durable-combinator rejections wrappedCancelledError/suspension into a retryableRestateErrordefect, causing cancelled invocations to be silently retried. -
@overeng/genie: Add
projectionArtifact.json()for schema-versioned deterministic JSON projections, with generic validation hooks and reusable duplicate-value validators for TS-authored data projected into committed JSON. -
Notion docs: Add lightweight package-level VRS requirements/spec docs for
@overeng/notion-core,@overeng/notion-effect-schema, and@overeng/notion-effect-client; align the broader Notion VRS docs with implementation reality while keeping package READMEs user-facing. -
@overeng/notion-core: Add a dependency-free shared primitive package for Notion API constants, UUID helpers, color/property tuples, property write-class classification, and raw rich-text plain-text extraction.
-
devenv-modules/tasks/changesets: New shared task module providing
release:changeset:check-bodies, which rejects malformed Changesets where the YAML frontmatter has no package bumps and the body is empty. Catcheschangeset add --emptyinvocations whose---\n---\nplaceholder was never filled in. Consume via(inputs.effect-utils.devenvModules.tasks.changesets { })indevenv.nix. Ported from livestorejs/livestore#1269. -
@overeng/notion-cli: Consolidate the Notion command surface under the packaged
notionbinary, includingnotion dbreplica commands, reusablenotion mdcommand composition, shared--versionidentity, and Nix/devenv exposure. -
@overeng/notion-datasource-sync: Add a standalone Notion datasource sync package with Effect/Schema contracts, a self-contained SQLite replica (
<database-id>.sqlite), planner/guard/conflict logic, fake and live Notion gateways, NotionMD body integration, one-shot and watch sync surfaces, progress UI, OpenTelemetry instrumentation, and broad fake/live E2E coverage. -
@overeng/notion-datasource-sync: Add guarded public SQLite write surfaces for rows, metadata, relations, archive/restore, body pushes, conflict resolution, external URL file attachment staging, view diagnostics, explicit
sync_status.statebuckets, and replica-derivednotion db export. -
@overeng/notion-datasource-sync: Add live demo and verification support for provisioned synthetic Notion fixtures, durable scratch cleanup ledgers, read-after-write body settlement, request/rate-limit telemetry, and credential-free manifest guard coverage.
-
@overeng/notion-md: Add a public body-only facade for adapters to observe, read, materialize, verified-replace, and settle
.nmdbodies without depending on sync internals. -
@overeng/notion-md: Add path-level
statusPath/planPath/syncPathAPIs that route file, directory-tree, and flat batch targets through the same contract as the CLI, with library-level guards that reject single-file operations on managed tree members. -
@overeng/notion-md: Keep the recursive tree engine behind the path-level API by exporting
syncPath/planPathas the public surface instead of the lower-levelsyncTreeoperation. -
@overeng/notion-md: Include created page identity (
pageIdandurl) in applied treecreateresults, and document the valid unbound tree.nmdshape for local-first page creation. -
@overeng/notion-md: Preserve authored directory-tree child indexes when parent bodies contain block-level
<page>anchors, support URL-less placeholder anchors for newly created children, and fail closed on missing, duplicate, or dangling child anchors. -
@overeng/react-inspector: Lineage annotation namespace (#687). New
Lineagemodule withSourceOfTruth | Derived | Projection | Cache | Mirror | External | Computedtagged union, plus composable companion annotations (Authority,Freshness,ForeignKey). All annotations are self-describing Effect Schemas with ergonomicpipe-style constructors (Lineage.derivedFrom,Lineage.cache,Lineage.authority, etc.). The schema-aware renderer surfaces a small superscript glyph next to annotated field names and a dedicatedLINEAGE/AUTHORITY/FRESHNESS/REFblock in the schema tooltip.SchemaInfogains an optionallineage: LineageBundlefield. Source-field path references inDerived.fromcarrydata-lineage-targetattributes for future jump-to-source wiring. Round-trip-tested via vitest. -
@overeng/react-inspector: Map/Set container labels (#686).
Schema.Map({key, value})renders asMap<K, V>(N),Schema.Set(T)asSet<T>(N), plus theReadonly*variants. Detected via theeffect/annotation/TypeConstructorannotation onDeclarationASTs. -
@overeng/react-inspector: Runtime tagged-union narrowing (#686). When a field's declared schema is
Schema.Union(A, B, C)of_tag-discriminated variants and the runtime value carries a matching_tag, the inspector narrows the display (name, tooltip, container label, nested field resolution) to the matched variant. Narrowing happens on every path segment, not just the leaf, so nested fields under a tagged union resolve through the matched variant.SchemaProvidergains arootDataprop and the context exposes a newgetContextForPathWithValue(path, value)method. -
@overeng/react-inspector: Schema-derived container labels for arrays, records, and tuples (#686). Arrays show
Array<Item>(N)instead ofArray(N), records showRecord<string, Money>instead ofObject, tuples show[string, number, boolean]. Named array/record schemas (.annotations({ identifier: ... })) take precedence over the constructed label.SchemaInfogains acontainerLabel?: stringfield.getFieldSchemanow falls back toindexSignature.typeso per-field schema resolution works inside records. -
@overeng/react-inspector: Rich schema annotation tooltips. Hovering or keyboard-focusing a field name (or struct type badge) now shows a tooltip surfacing
description,examples,default, refinement-derived constraints (min/max/length/pattern/format/...), and possible values forLiteral/Enums/Union-of-literal /TemplateLiteralASTs. Replaces the previous nativetitle=attribute. New exports:SchemaTooltip,SchemaInfo,getSchemaInfo,getConstraintsFromJSONSchema,getPossibleValuesFromAST.getFieldSchemano longer eagerly unwraps refinement/transformation wrappers so user-supplied annotations on those wrappers reach the tooltip. -
@overeng/genie:
githubLabels()runtime primitive for declarative GitHub Issue/PR label management (color, description, deprecation, legacy migrations). Consumed bymq-cli repo labelsinschickling/dotfiles. -
genie/external.ts: Shared label catalog exports (
commonLabels,mqLabels,andonLabels,deprecatedDefaults,legacyMigrations) for cross-repo label IaC. Effect-utils self-applies via.github/labels.json.genie.ts. -
@overeng/notion-effect-client: Add database create/update/archive helpers and switch live Notion integration tests to provision isolated per-run fixtures under
NOTION_TEST_PARENT_PAGE_IDinstead of relying on stale hard-coded workspace page/database IDs. -
@overeng/notion-md: Add managed workspace materialization.
sync <dir> --from-remote --root <page-id-or-url>establishes a workspace from a Notion page tree, and latersync <dir>materializes newly discovered remote child pages while reusing the existing guarded one-page sync engine. -
@overeng/notion-react: JSX-driven page operations for root
<Page>and sub-page<ChildPage>(#618). Root<Page>acceptstitle/icon/coverand drivespages.updateon the sync root.<ChildPage>becomes a first-class sync boundary withtitle/icon/cover/children/blockKey; the sync driver emits and executescreatePage,updatePage,archivePage, andmovePageviaNotionPages.*with inline block packing (depth ≤ 2, ≤ 100 blocks), tail block ops scoped to the new page, and partial-create rollback on tail failure. Each sub-page is its own sync boundary with its ownblockKeynamespace, anddiff()descends recursively through retained sub-pages. -
@overeng/notion-react: Opt-in
reorderSiblingsonsync()(#618 phase 4d). Intra-parent<ChildPage>reorder lands via a singlereorderPagesop that the driver realizes with 2Npages.moveroundtrips through a holding parent (Notion'spages.moverejects same-parent, but a trip out and back bumps the page to the end of the original parent'schild_pageblock list). Acceptstrue(library auto-provisions and archives a scratch page per sync-with-reorder) or{ holdingParentId }(caller-owned lifecycle). Defaultfalsepreserves the pre-4d contract: retained-but-reshuffled siblings still emit same-parentmovePage, the API rejects, and the driver swallows the validation error. -
@overeng/notion-cli: Expose
notionbinary via Nix flake (packages.${system}.notion-cli) so consuming repos can add it to their$PATHwithout managing JS module resolution themselves -
@overeng/pty-effect/client: Add PTY client support for session tags,
getSession,gc,updateTags,sendData,queryStats,readRecentEvents, and live event following -
@overeng/notion-effect-schema: Add
NamedIcon(type:"icon") variant toIconunion for native Notion icons (noticons) (#543) -
@overeng/notion-effect-schema: Add
NoticonColorschema for named icon color palette -
@overeng/notion-effect-schema: Add
heading_4,tab, andmeeting_notesblock types toBlockType -
@overeng/notion-effect-schema: Add optional
is_lockedfield toPageandDatabaseSchema -
@overeng/notion-effect-client: Add
BlockInsertPositiontagged union (after_block,start,end) for block insertion -
@overeng/notion-effect-schema: Add full
DataSourceSchemaforGET /data_sources/:id(properties, parent, database_parent, etc.) -
@overeng/notion-effect-schema: Add
PageMarkdown,Comment,CommentParent,View,ViewTypeschemas -
@overeng/notion-effect-schema: Add
RelativeDateschema type for query filter values (today,tomorrow, etc.) -
@overeng/notion-effect-client: Add
NotionDataSourcesmodule withretrieve(),create(),update() -
@overeng/notion-effect-client: Add
NotionCommentsmodule withcreate(),list(),listStream() -
@overeng/notion-effect-client: Add
NotionViewsmodule withretrieve(),list(),listStream(),create(),update(),delete() -
@overeng/notion-effect-client: Add
getParagraphIcon()helper for tab paragraph block icons -
@overeng/notion-effect-client: Add
NotionCustomEmojismodule withlist()for workspace custom emojis -
@overeng/notion-effect-client: Add
NotionPages.getMarkdown()andNotionPages.updateMarkdown()for server-side markdown API -
@overeng/notion-effect-client: Add
NotionPages.move()for moving pages between parents -
@overeng/notion-effect-client: Add
markdownoption toCreatePageOptions(alternative tochildren) -
@overeng/notion-effect-client: Add
is_lockedanderase_contenttoUpdatePageOptions -
@overeng/notion-effect-client: Add
filterPropertiesandinTrashto data source query options -
@overeng/notion-effect-client: Add strict
.nmdfrontmatter schemas and a storage-size classifier for Notion enhanced Markdown sync metadata -
@overeng/notion-md: Add prototype
notion-mdCLI package for self-contained.nmdpull/status/push flows with guarded conflict detection and sidecar escalation tests -
@overeng/notion-md: Add live Notion E2E coverage for pull/status/push/conflict detection and wire it into the Notion integration CI job
-
@overeng/notion-md: Expose
notion-mdas a Nix flake package with managed pnpm dependency hash refresh support -
@overeng/notion-md: Harden push safety for unknown blocks, Roughdraft review markup, body conflicts with base snapshots, and explicit typed property writes
-
@overeng/notion-md: Add conservative automatic three-way body merge for non-overlapping line edits, insertions, and deletions
-
@overeng/notion-md: Replace ad hoc sidecar/base files with strict frontmatter object refs and an Effect-native content-addressed
.notion-mdstate store -
@overeng/notion-md: Use Notion Markdown
update_contentfor proven unique body edits, with guardedreplace_contentfallback and live Notion E2E coverage -
@overeng/notion-md: Extract body merge/update planning into a focused pure module with unit coverage
-
@overeng/notion-md docs: Consolidate scattered research/spec notes into the package-local VRS docs under
packages/@overeng/notion-md/docs/vrs/ -
@overeng/notion-md docs: Add package-local usage docs for getting started, CLI workflows,
.nmdformat, sync safety, and troubleshooting -
@overeng/notion-md: Add a durable Notion live E2E run ledger and a committed demo
.nmdfixture synced with the automated Notion showcase page -
@overeng/notion-md: Push modeled page metadata from strict frontmatter, including page lock/trash state plus writable icon and cover shapes, and add typed
place/verificationproperty frontmatter values -
@overeng/notion-md docs: Fold the remaining VRS design decisions into
spec.mdand remove the companion question log -
@overeng/notion-md: Add a TUI Storybook for CLI output states and wire it into the shared Storybook task registry
-
@overeng/tui-stories: Export
tui-storiesCLI as a Nix package via the flake (#525)
-
@overeng/utils-dev/node-vitest: Fix
makeOtelVitestLayerposting traces to the WRONG URL, which made the OTLP trace exporter self-disable SILENTLY.OtlpTracer.layer({ url })(andOtlpLogger/OtlpMetrics) POSTsurlVERBATIM through the sharedotlpExporter(HttpClientRequest.post(url)) — only the combinedOtlp.layer({ baseUrl })appends the/v1/tracessignal path (viaappendUrl).makeOtelVitestLayerpassed a BARE base endpoint as the tracerurl, so the exporter POSTed to the receiver root, 404'd, and disabled itself — traces never landed, with no error. Now it builds the full traces URL via a new exportedotlpTracesUrl(baseEndpoint)(${endpoint}/v1/traces, trailing-slash normalized), the single source of truth the bridge and a regression test both lock.makeOtelVitestLayeralso gains an additiveendpointoption (explicit base URL, wins overendpointEnvVar) so the capture bridge can point it at the in-process receiver; existing env-var callers are unaffected (#769, #772). -
@overeng/notion-effect-client: Make live
NotionBody.observefail closed across concurrent remote edits by bracketing Markdown and block-tree reads with page metadata retrieval, retrying unstable observation windows up to three total attempts, and returningNotionBodyObservationChangedErrorwhen all attempts seelast_edited_timechange (#761). -
@overeng/notion-md + @overeng/notion-datasource-sync: Fail closed when a remote Notion Markdown body observation is lossy, including endpoint truncation, empty endpoint bodies with non-empty rendered evidence, unknown blocks, unsupported body inventory, or rendered suffix content omitted after dividers and toggleable headings, so partial bodies are not adopted as clean
.nmdbases or settled through datasource-sync body guards (#759). -
@overeng/notion-md: Adopt block-tree-rendered Markdown, not endpoint Markdown reparsed through CommonMark, as the live pull clean-base body so divider/heading-dense pages do not turn paragraph blocks into
##headings (#763). -
@overeng/restate-effect: Route the standalone blocking durable awaits — the awakeable
promiseand the durable-promiseget/peek— through the sharedawaitDurableseam (PR #760, two Codex P1 review threads). They previously wrapped EVERY rejection into aRestateErrordefect viatryPromise + orDie, bypassing the seamrun/sleep/timeout/all/race/anyuse to classify cancellation/suspension/terminal/infra. The load-bearing fix: aDurablePromise.reject(andAwakeable.reject/ingress.rejectAwakeable) now makes the awaitingget/promisefail TERMINALLY — the rejection'sTerminalErrorterminalizes the awaiter VERBATIM (R33/R34) instead of degrading into a retried infra defect (which Restate retried forever). Cancellation of these awaits now interrupts (finalizers run, mapped to a non-retriedCancelledError) rather than being wrapped into a retried defect.awaitDurablegains an opt-in'terminal-reject'mode for this (therun/sleepinfra paths keep actx.rungive-up'sTerminalErroras an infra defect, since a step give-up is infra, not a domain reject). The doc comment that claimed the old code terminalized a reject verbatim (it did not) is corrected to match. Covered bysrc/suspension.integration.test.tsagainst a nativerestate-server: aDurablePromise.rejectterminalizes the awaitinggetand therunis NOT retried (the decisive falsifier — the bug retried forever / timed out), plus suspend-and-resume contract tests for the awakeable and durable-promise awaits underalwaysReplay+disableRetries. VRS: decision 0003, spec §1.3/§9.1.2, glossary. -
@overeng/restate-effect: Route the in-handler peer
Restate.call(call/objectClient/workflowClient) through the sharedawaitDurableseam, and thread the redaction cipher into descriptor-issued peer calls — closing the third Codex P1 review thread and the descriptor-redaction KNOWN LIMITATION from the contract-invocation-policy change (PR #760). (1) Suspension/cancellation preserved for in-handler calls:callRpcpreviously wrapped thectx.genericCallInvocationPromiseinEffect.tryPromise, so an unresolved peer call's SUSPENSION sentinel (the invocation must park and resume on the result) or aCancelledErrorwas converted into aRestateErrordefect — degrading a park-and-resume into a defect→retry and swallowing cancellation. It now routes throughawaitDurablein'terminal-reject'mode: a suspension re-throws verbatim (the SDK parks/resumes), a cancellation interrupts, and a calleeTerminalErrorterminalizes the caller VERBATIM (R34) instead of becoming a retried infra defect (which Restate retried forever). A peer call carries no typed failure, soRestate.call/objectClient/workflowClientare now honestly typedEffect<A, never, RestateContext>(matching the descriptor call path andRestate.run). (2) Descriptor-path redaction:Restate.callDescriptor/objectCallDescriptorbuilt their serdes with NO cipher, so aRestate.sensitivefield on a descriptor-issued peer call insideRestate.all/race/anythrewRedactionCipherMissingErroreven with aRestateRedactionlayer present. The cipher is now resolved at ISSUE time by the effectful combinator and threaded throughDescriptor.issue(a descriptor builder is synchronous and cannot resolve the ambient cipher itself), so the descriptor path encrypts a sensitive field identically to the directcallRpcpath (decision 0020). Covered bysrc/suspension.integration.test.ts(a callee that fails terminally terminalizes the caller's call, NOT retried — the decisive falsifier, alongside the durable-promise/awakeable cases) andsrc/schema/redaction.integration.test.ts(a descriptor peer call with a sensitive field round-trips throughRestate.allunder a realrestate-server). VRS: decision 0003/0020. -
@overeng/restate-effect: Fix an intermittent
pollLoopintegration flake (scheduled.integration.test.ts"basic recurrence") under high CPU contention. The "exactly-once" checkdomain n === control-plane iterationwas sampled mid-flight from two non-atomic ingress reads against a still-advancing loop, so a cycle landing between the reads madenleaditerationby one. The primitive is correct (cycles are strictly serialized and run exactly once — verified, no extra/duplicate cycle); the assertion was the wrong invariant. Moved the equality check to quiescence (afterstop, when both counters are frozen) so it stays a fulln === iterationinvariant without any tolerance band or sleep band-aid. -
@overeng/notion-md: Guard unified tree sync planning and destructive operations by reporting dry-run moves, blocking missing-file trash unless forced, checking tree
replace_contentraces, and documenting the explicit tree/flat-batch CLI contract. -
@overeng/notion-md: Deduplicate
--from-remotematerialized paths for colliding Notion titles, strip derived child anchors from tree file bodies while preserving composed baselines, and keep tree sync pinned to the current strict index schema. -
CI / Nix packages: Refresh stale pnpm fixed-output hashes for
oxc-config,genie,notion-cli,notion-md,megarepo,workflow-report, andtui-stories; registernotion-corein workspace checks; format PR-touched files and keep oxlint fatal for error-level diagnostics while the existing warning backlog is tracked separately. -
secretspec: Keep the public repository secretspec limited to environment variable declarations by removing machine-specific secret locator metadata.
-
genie/ci-workflow: Match managed workflow report PR comments by hidden
stateIdbefore patching so independent reports sharing the default marker cannot overwrite each other. -
devenv/tasks/shared/pnpm: Share live and fixed-output pnpm install policy, cap live install concurrency to match the prepared-workspace builder, and accept Darwin pnpm teardown exits only after materialization is proven complete.
-
@overeng/megarepo: Keep store/test integration fixtures independent of user tag-signing Git config by creating fixture tags with
--no-sign, avoid slow filesystem-watch semaphore acquisition in store locks, letmr store gc --output jsontake the final-state path directly, mergegit worktree listwith the on-disk store layout so GC never drops real worktrees from discovery, and run the megarepo Vitest suite with file parallelism disabled because the in-process CLI integration harness mutates globalprocess.envand stdio. -
devenv/tasks/shared/nix-cli: Run aggregate
nix:checkpackage hash validations sequentially so CI does not fan out multiple full root-workspace pnpm FOD rebuilds at once on Darwin. -
nix/workspace-tools: Tighten pnpm child/network concurrency inside fixed-output pnpm deps builds and cap Darwin Node heap during the install step so macOS CI is less likely to die with an unstructured
Killed: 9while materializing whole-workspace install roots. -
@overeng/pty-effect: Make the server-mode attach/read integration test wait briefly before emitting its marker so slower Linux CI runners do not miss one-shot startup output during initial attach replay.
-
@overeng/notion-datasource-sync: Keep public SQLite replicas coherent after direct cell edits by routing row changes through canonical CDC, refreshing scalar readback, and failing closed for invalid or unsupported mutations.
-
@overeng/notion-datasource-sync: Add the missing exported API JSDoc and explicit boolean comparisons that kept
lintanddevenv-perfred on PR #683. -
nix/oxc-config-plugin: Refresh the
oxc-configpnpm fixed-output hash sooxlintcan build again in CI andlint/devenv-perfstop failing on the stale dependency boundary. -
nix packages: Refresh stale pnpm dependency hashes for the Genie, megarepo, tui-stories, and notion-md CLI packages.
-
nix packages: Refresh the stale
megarepo,tui-stories,notion-md, andworkflow-reportpnpm dependency hashes sonix-check,nix-fod-check, closure-size, and Storybook-report CI jobs use the current dependency closures again. -
nix packages: Refresh the stale
notion-clipnpm dependency hash after adding the datasource-sync runtime to the packaged workspace. -
genie/packages: Include
@overeng/notion-datasource-syncin the internal package catalog so generated workspace dependency metadata covers the new package. -
pnpm task: Bound macOS CI pnpm install heap usage and tolerate Darwin teardown exit 137 only after node_modules materialization is complete.
-
devenv/tasks/shared/pnpm: Share live and fixed-output pnpm install policy, cap live install concurrency to match the prepared-workspace builder, and accept Darwin pnpm teardown exits only after materialization is proven complete.
-
@overeng/notion-datasource-sync: Improve watch and remote-adoption reliability by classifying absence candidates with direct retrieval, clearing stale repair markers, honoring gateway retry pacing, and reusing complete checkpoints for lower-latency incremental polling.
-
@overeng/notion-effect-client: Parse
retry-afterrate-limit header even whenx-ratelimit-remainingis absent, so rate-limit retry guidance is preserved whenever headers provide it. -
@overeng/notion-effect-client: Parse
Retry-AfterHTTP-date values as well as seconds and clamp malformed/negative values to zero, so retry backoff avoids invalid or ambiguous rate-limit guidance. -
@overeng/notion-datasource-sync: Harden public SQLite
changessemantics for row create/archive/restore, cell writes, body pushes, metadata/schema/conflict-resolution tables, coalesced repeated edits, ambiguous create outcomes, and fail-closedpeople/filesdirect edits. -
@overeng/notion-cli: Gate database export live fixture tests on writable fixture configuration and provision shared Notion integration fixtures before reading
TEST_IDS, avoiding accidental live API calls with empty IDs when onlyNOTION_API_TOKENis present. -
nix/oxc-config-plugin: Refresh the pnpm dependency fixed-output hash so the devenv shell can realize the oxlint package used by
check:all. -
@overeng/tui-react: Let Effect CLI own Ctrl-C entrypoint handling for
run(App, handler). Apps whose action schema includesInterruptednow dispatch it during normal Effect interruption finalization, map interrupt-only CLI exits to code 130, and suppress noisy interrupt-only error output inrunTuiMain. -
@overeng/megarepo: Stop store repository discovery from walking internal scratch roots like
tmpbefore scanning repos, yield during repository discovery so Effect interruption can propagate promptly, and tighten CLI OTel flush timing so interrupted TTY commands return quickly while still exporting traces. -
@overeng/megarepo: Improve
mr store gc --dry-run --output ttyprogress UX with early phase updates, heartbeat refreshes, realtime worktree discovery/active-check counts, explicit interrupted output, exit code 130 for Ctrl-C, and more granular OTel spans for removal status checks. GC removal checks now use a singlegit status --untracked-files=normaldirty preflight before the upstream check, avoiding expensive recursive untracked-file enumeration while still failing closed for dirty worktrees. -
@overeng/megarepo: Make store GC worktree discovery layout-authoritative across branch, tag, and commit ref roots, and add OTel/log visibility when
git worktree listcannot be read. -
@overeng/megarepo: Avoid recursive
mr fetch --apply --allhangs when nested apply falls back from a detached branch worktree to an already-created commit worktree. -
@overeng/megarepo: Make
mr store gcdata-loss safe for shared stores.- Tracks workspace liveness in a store-local registry and protects both active
repos/*symlink targets and lock-derivedrefs/heads/*/refs/commits/*paths. - Keeps named
refs/heads/*andrefs/tags/*worktrees by default while reclaiming clean unrootedrefs/commits/*worktrees. - Removes the temporary managed/unmanaged store metadata model and the
--include-unleasedGC mode. - Forces untracked-file detection during worktree status checks so user/global Git config cannot hide untracked work from GC.
- Skips worktrees whose git status cannot be inspected unless
--forceis passed, preserving the fail-closed deletion policy. - Acquires worktree locks before removal and reports deletion errors as
errorinstead ofremoved. - Discovers store repositories by
.bare/presence instead of assuming onlyhost/owner/repopaths, traverses discovery concurrently, skips dirty checks for named refs protected by default, streams GC progress through TTY/NDJSON output, avoids recursive worktree-content scans during GC discovery, prunes Git worktree metadata once per repo after safe removals, and adds OTel spans for GC, liveness, and repo discovery.
- Tracks workspace liveness in a store-local registry and protects both active
-
@overeng/react-inspector: Render the schema display name exactly once in collapsed schema-aware object previews (#684).
SchemaAwareObjectPreviewis now the single owner of the schema title (rendered in the object-description slot, italicized when sourced from atitle/identifierannotation); the collapsed branch inSchemaAwareNodeRendererno longer prefixes a duplicate copy. Fixes0: Source Origin Summary Source Origin Summary {…}→0: Source Origin Summary {…}. -
@overeng/notion-cli: Gate
db dumplive fixture tests on writable fixture configuration and provision shared Notion integration fixtures before readingTEST_IDS, avoiding accidental live API calls with empty IDs when onlyNOTION_API_TOKENis present. -
nix/oxc-config-plugin: Refresh the pnpm dependency fixed-output hash so the devenv shell can realize the oxlint package used by
check:all. -
devenv/tasks/shared/nix-cli: Make
dt nix:hash:*update nesteddepsBuilds.".".hashentries used bymkPnpmCli- Lets CLI package hash refreshes converge again after repo-root
pnpm-lock.yamlchanges instead of looping until max iterations - Restores the intended
dt nix:hash:genieworkflow for package-version bumps that only need the fixed-output deps hash refreshed
- Lets CLI package hash refreshes converge again after repo-root
-
@overeng/notion-react: Route
<ChildPage>title updates throughpages.updateinstead ofblocks.update(#618). Notion'sPATCH /v1/blocks/{id}rejects a{ child_page: { title } }body withvalidation_error; the sync driver now emitsPATCH /v1/pages/{id}with a properly-shapedtitleproperty forchild_pageupdates. -
@overeng/pty-effect/client: Fix flaky timeout in
followEvents(#577) —asyncScoped's setup ran lazily inside the forked consumer fiber, missing events fired before the fiber started. Replaced withStream.asyncPush(setup still lazy, butemit.singleis now correctly synchronous forfs.watchcallbacks). Test updated to watchsession_exitinstead ofsession_start, sinceEventFollower.watchFilestarts reading at the current end-of-file when a new session is discovered, makingsession_startunreachable via live following. -
@overeng/notion-md: Verify content-addressed object bytes exactly, reject object-store inventory mismatches, and emit structured watch errors as compact JSON lines
-
@overeng/notion-md: Allow property-only pushes across concurrent remote body edits, clear stale unknown-block storage after destructive replacements, and normalize object-ref path checks cross-platform
-
@overeng/notion-md: Route watch file events through Effect Platform
FileSystem.watchwhile preserving scoped cancellation, polling, debounce, and recoverable sync-error behavior -
@overeng/notion-md: Add batch multi-file and recursive folder orchestration for
status,push, andsync, including duplicate page-id preflight, per-file result envelopes, bounded concurrency, and multi-file watch mode -
@overeng/notion-md docs: Add a recursive workspace demo template that shows multi-file folder sync setup without committing placeholder pages as live targets
-
@overeng/notion-md: Give CLI subprocess e2e checks explicit timeouts so CI load does not fail the help-path smoke test at Vitest's default 5s limit
- @overeng/restate-effect: Relocate the VRS design docs (vision/requirements/spec/glossary + the
decisions/records) into adocs/vrs/subdir, separating the design docs from the user-facing README/examples. Intra-VRS relative links are preserved (the whole tree moved together); external references (README,src/Serde.ts,src/Annotations.ts) updated to the newdocs/vrs/paths. Docs-only — no library surface change. - @overeng/restate-effect: Sharpen the durable surface (refinement Core A). (1) The durable combinators (
Restate.run/sleep/timeout/all/race/any/State.*/Awakeable.make().promise) now have a CLEAN typedE— they carry NORestateError. An infra failure isEffect.die'd at the singleawaitDurableseam and classified at the boundary (transient → Restate retries; terminally-failed step → fail), so the no-opcatchTag('RestateError', Effect.die)is gone and only the inner effect's own domainEflows throughRestate.run(Restate.run(name, action: Effect<A,Ea,R>) → Effect<A, Ea, R'>).Restate.runjournals the raw success value (not a wrappedExit). AddsRestate.runExit(name, effect) → Effect<Exit<A,E>>as the opt-in observe form for compensation/sagas (the infra failure is aCause.Diecarrying theRestateError). TheIngressFailedclient surface (Restate.call/send, ingress) keeps its typedRestateError(it pairs with the typed decode helper). Aligns the impl with decision 0003. (2) Every durable op now exposes a DESCRIPTOR for the deterministic combinators:Awakeable.make(S).descriptor,Restate.callDescriptor/objectCallDescriptor, alongside the existingrunDescriptor/sleepDescriptor/DurablePromise.for(S).getDescriptor— so an awakeable joinsRestate.all/race/anyin journal-source order (replacing the in-processEffect.raceFirstworkaround that lost determinism). (3)Restate.retryable({ retryAfter })acceptsnumber | Duration | ((error) => number | Duration | undefined)— a static shorthand OR an instance projection read off the actual failing error at the boundary (e.g. a 429'se.retryAfterMillis), mirroringidempotencyKey. (4) Papercuts:State.foracceptsSchema.optionalfields; theStateRead/StateWrite/DurablePromisecapability markers carry descriptive brands so a violation reads like the missing capability; the boundary validates a thrown failure against the declarederrorunion (Schema.encodeUnknownEither) and surfaces a non-match as a defect (no silent mis-encode). (5) Type frictions: a heterogeneous-AppRservicesarray now typechecks (the_Implementation._AppRphantom is covariant +layer/serveinfer theAppRUNION via anAppROf<Services>extractor);./testingharness.ingress.*preserves the precise per-call typed-error + success channels (theBindLastwrapper that collapsedErrorOfis replaced by explicit genericBoundIngresssignatures derived from the*Ofhelpers). VRS: decisions 0003 + 0011 and spec §5/§6/§6.2/§7/§13 updated; README/examplesTODO(refinement)stubs for retryAfter, the clean-error-channel ergonomics, and awakeables-in-racefilled with verified examples. No new dependencies;dt check:allgreen. - @overeng/utils + @overeng/notion-md + @overeng/notion-datasource-sync: Deduplicate SHA-256 content hashing onto the shared isomorphic
sha256Hexhelper in@overeng/utils, dropping directnode:cryptouse innotion-md(sha256Digest) andnotion-datasource-sync(hashStoreBytes). Output is byte-identical; the hashing is now browser-capable. - @overeng/utils + @overeng/notion-md + @overeng/notion-datasource-sync: Extract one canonical
titleSluginto@overeng/utils/isomorphic/stringand converge both Notion workspace path generators onto it.notion-mdadopts the NFC-normalized, 120-char-capped slug semantics for newly generated page paths (existing manifest-recorded paths are unaffected). - @overeng/notion-effect-client + @overeng/notion-cli + @overeng/notion-md + @overeng/notion-datasource-sync: Consolidate Notion token resolution into a single
resolveNotionToken(returningRedacted) plus aNotionTokenMissingtagged error in@overeng/notion-effect-client, replacing three independent resolvers across thenotion,notion md, andnotion dbCLIs. The accepted env-var set (NOTION_API_TOKEN→NOTION_TOKEN) is now a single source of truth. - @overeng/notion-effect-schema: Unify the markdown and HTML rich-text annotation formatters behind one shared ordering combinator (
applyAnnotations); output is unchanged. - @overeng/notion-effect-schema + @overeng/notion-effect-client: Reuse dependency-free Notion constants, literal tuples, UUID helpers, property classification, and raw rich-text helpers from
@overeng/notion-corewhile preserving existing schema/client public exports. - @overeng/notion-effect-schema + @overeng/notion-datasource-sync: Promote the bidirectional Notion property-value codec and write-class taxonomy into
@overeng/notion-effect-schema(CanonicalPropertyValue,makeCanonicalCodec({ hash }),decode/encodeCanonicalPropertyValue,propertyWriteClassFromType,Canonical{Decode,Encode}Error), with hashing injected by the caller so the schema package owns the data semantics whilenotion-datasource-synckeeps the hashing policy. nds now delegates property decode/encode to it and keeps only its sync-domain projection/guards; the canonical id brands (Notion.PropertyId/Notion.PageId/Notion.PropertyName) live in the schema package and are aliased by nds. Canonical JSON output — and therefore content hashes — is byte-identical. - @overeng/notion-effect-client + @overeng/notion-datasource-sync: Consolidate Notion API mechanics into the client. Add an optional composable request-throttle layer (
NotionThrottle/NotionThrottleLive, token-bucket viaRateLimiter, applied once per logical request rather than per retry) and move the rate-limit classification (isRateLimited/retryAfterMillis) ontoNotionApiError. The datasource-sync gateway drops its hand-rolled global throttle and configures the client layer instead, with production wiring keeping the existing 3 rps. Replace the unusedpaginatedStreamwith apaginatehelper over the mappedPaginatedResultshape (optional initial cursor + item/page emit modes) and migrate all cursor-pagination sites (clientviews/databases, gateway views/rows/page-property) onto it. - genie/external.ts: Drop
injectWorkspacePackages: truefrom the sharedcommonPnpmPolicySettings. The setting is required for effect-utils' own pure Nix/FOD package closure model, but downstream consumers without that model lose visibility to workspacedevDependencies/peerDependenciesat type-check time once pnpm injects copies of each workspace package without their dev/peer closures (see livestorejs/livestore#1271). The setting now lives oncommonPnpmWorkspaceDataingenie/internal.ts, so effect-utils' own root yaml still materializes injected workspace packages while peer repos spreadingcommonPnpmPolicySettingskeep pnpm's default symlink resolution. Repos that want injection can addinjectWorkspacePackages: trueexplicitly in their ownpnpm-workspace.yaml(or extendcommonPnpmPolicySettingswith it). - @overeng/notion-datasource-sync docs: Compact standalone VRS decision records into the active requirements/spec/capability-boundary documents and remove release/checklist artifacts from the VRS companion docs.
- @overeng/notion-datasource-sync + @overeng/notion-md: Route the NotionMD-backed body adapter through the public body facade and centralized datasource-sync sidecar helpers, removing direct
.nmdparsing/materialization glue from datasource-sync while preserving body-only semantics. - @overeng/notion-effect-client / @overeng/notion-md: Share canonical
.notion-mdobject-store paths, sync-state paths, object refs, and storage-size thresholds from the NMD schema layer so local metadata decisions derive from one source of truth. - @overeng/notion-md: Breaking CLI simplification: collapse the user-facing page workflow around
syncandstatus; replace the old explicitpull/pushentrypoints withsync <page-id-or-url> <file.nmd>for bootstrap and guardedsync <file.nmd>for reconciliation. - @overeng/notion-md: Remove legacy compatibility paths for batch
pushand local-firstpage_id: nullpage creation; existing Notion pages must be materialized withsync <page-id-or-url> <target>. - @overeng/pty-effect/client:
spawnDaemonnow delegates to@myobie/pty.spawnDaemoninstead of duplicating the daemon spawn pipeline. The Bun-on-Node case is routed through upstream's newlauncheroption (still honorsNODE_BIN). Eliminates a divergent in-house spawn path so consumers automatically inherit upstream improvements such as bundle-safe spawn (compoundingtech/pty#38). Public API andPtyDaemonSpecschema unchanged. - @overeng/notion-react:
<Page>and<ChildPage>accepticon={null}andcover={null}as explicit clear sentinels (#618). Dropping the prop is still "no claim" (preserves server state); passingnullemitspages.update({icon: null})/pages.update({cover: null}). On a fresh page with no prior icon/cover,nullis a no-op. - @overeng/notion-react: Same-parent
<ChildPage>creates are now sequential — JSX order is preserved 1:1 on the server (#618). Parallelpages.createunder a common parent yields nondeterministicchild_pageordering; the driver issues sequential POSTs so no post-create re-fetch is needed. T08 (formerly "concurrent sibling-page order is not authoritative") is now a normative invariant; the deferredensureSiblingOrdersync option is dropped. - @overeng/notion-react:
CACHE_SCHEMA_VERSIONbumped2 → 3to accommodate per-page cache subtrees (#618). v2 caches fall through the existing"schema-mismatch"cold path — transparent, no caller action required. The first sync after upgrade may emit one spurious metadata update per sub-page as response-normalized title/icon/cover is recomputed. - genie/ci-workflow: Unify Vercel CI job generation behind a single
vercelDeployJobs()helper- Removes the separate static-job and job-merge helpers now that task-level deploy mode is already unified in
vercel.nix - Lets consumers mix build-mode and static-mode deploys in one project list and attach per-project pre-deploy setup like Vercel git-author configuration
- Removes the separate static-job and job-merge helpers now that task-level deploy mode is already unified in
- deps: Upgrade
@myobie/ptyfrom the old git-pinned fork to the published0.8.0release line - @overeng/notion-effect-client: Upgrade Notion API version from
2022-06-28to2026-03-11 - @overeng/notion-effect-schema: Remove
archivedfield fromDatabaseSchema,Page, andBlockschemas (replaced byin_trashin API 2026-03-11) - @overeng/notion-effect-client: Replace
afterparameter withpositionobject inAppendBlockChildrenOptions - @overeng/notion-effect-client: Replace
archivedwithin_trashinUpdatePageOptionsandarchive()method - @overeng/notion-effect-client: Remove
archivedfromTypedPageinterface (useinTrashinstead) - @overeng/notion-effect-client: Add named icon variant to
CreatePageOptionsandUpdatePageOptionsicon types - @overeng/notion-effect-client: Unify file upload API version with shared
NOTION_API_VERSIONconstant - @overeng/notion-effect-client: Update search filter from
'database'to'data_source'(API 2025-09-03+ change) - @overeng/notion-effect-client: Migrate database query from
/databases/:id/queryto/data_sources/:id/query(databaseId→dataSourceId) - @overeng/notion-effect-schema: Add
data_source_idparent variant toPageParentschema - @overeng/notion-effect-schema: Add
data_source_idparent variant toBlockParentschema for blocks returned from data-source-backed pages. - @overeng/notion-effect-schema: Rename
DataSource→DataSourceReffor lightweight reference inDatabaseSchema.data_sources - @overeng/notion-effect-client: Widen
SchemaHelpersto accept bothDatabaseSchemaandDataSourceSchema - @overeng/notion-md: Use
NOTION_API_TOKENas the only Notion credential environment variable across code, docs, tests, and SecretSpec
- genie/ci-workflow: Add a shared step decorator for job-local private Cachix read auth
- Creates a per-step netrc file and appends
netrc-filetoNIX_CONFIGinstead of relying on runner-global Determinate state - Lets downstream repos decorate
devenvand deploy run steps without exposing the Cachix token to unrelated actions
- Creates a per-step netrc file and appends
- devenv/tasks/shared/vercel.nix: Preserve dotfiles when packaging static prebuilt output for Vercel deploys
- Copies
staticDir/.into.vercel/output/static/instead of globbingstaticDir/*, so hidden assets and config files are not silently dropped
- Copies
- @overeng/notion-effect-client: Raise user integration-test timeouts to tolerate current Notion API latency in CI
- @overeng/notion-cli: Fix introspection pipeline to read properties from data source (API 2026-03-11 no longer returns properties on
GET /databases/:id) - @overeng/pty-effect/client: Keep daemon spawning on PTY's published client API while updating the wrapper to the current session/tag/event surface and preserving attach runtime context
- deps: Upgrade all Effect ecosystem packages (+2 minor each):
effect3.19.19 → 3.21.0,@effect/platform0.94.5 → 0.96.0,@effect/ai0.33.2 → 0.35.0, and 12 other@effect/*packages to latest - nix: Update
tsgoflake input toEffect-TS/tsgo@24a8a96(2026-03-30) - nix/workspace-tools: Replace committed per-package normalized pnpm lockfiles with direct staged installs from the authoritative root lockfile
- Keeps the full pnpm 11 multi-document root lockfile intact inside staged workspaces instead of checking in derived
pnpm-lock.normalized.yamlfiles - Keeps
manage-package-manager-versions=falseso pinned Nix pnpm builds stay sandbox-safe without self-bootstrapping another pnpm under$HOME - Removes first-party
pnpm-lock.normalized.yamlartifacts fromgenieandmegarepo
- Keeps the full pnpm 11 multi-document root lockfile intact inside staged workspaces instead of checking in derived
-
devenv/tasks/shared/ts.nix: Make
ts:check:strictinherit repo-localts:check.afterdependencies- Preserves consumer generators like
contentlayer:buildwhen strict typecheck is used as the CI gate - Prevents downstream repos from regressing when they already extend
ts:checkwith extra build prerequisites
- Preserves consumer generators like
-
genie/external: Export the shared
@effect-atom/atompeer-version allowlist in megarepo pnpm policy- Keeps downstream repos on
strictPeerDependencies: truewhile allowing the Effect version ranges already used inside effect-utils itself - Prevents consumer workspace installs from failing on the known pre-1.0 peer ranges declared by
@effect-atom/atom
- Keeps downstream repos on
-
genie/external: Export the full shared patch registry to peer repos
- Adds the
node-pty@1.1.0patch tocreatePnpmPatchedDependencies()/pnpmPatchedDependencies() - Unblocks composed-root
pnpm-workspace.yamlgeneration in downstream megarepos that import@overeng/utils
- Adds the
-
@overeng/genie: Use cwd-relative lock directory instead of shared
/tmp/genie-locks/to fixEACCESerrors in multi-user CI environments (#520) -
@overeng/tui-react: Format timeline timestamps as human-readable durations (e.g.
6m 18s / 16m 21s) instead of raw seconds (377.9s / 980.6s) inTuiStoryPreview(#472) -
devenv/tasks: make warm shell bootstrap commit-scoped and remove
ts:emitfrom shell entry- Adds an outer
setup:autocache so warmdevenv shellskips unchanged bootstrap work instead of traversingpnpm:install,genie:run, andmr:applyon every entry - Switches shell bootstrap from
mr:syncto initialmr:applyso a fresh worktree is normalized without fetching on every shell - Replaces setup fingerprint tool-version probes with resolved tool-identity hashing so warm shells do not pay
pnpm,genie, ormrCLI startup just to validate unchanged setup inputs - Speeds up warm task status paths by using direct
mr status, fingerprint-basedgenie:runcaching, a one-processpnpm:installprojection hash that preserves the previous structural guarantees, and ats:emitgraph that excludesnoEmitreferences at emit time - Hardens the fast paths by making the outer cache only track setup inputs while each task still verifies its own outputs before skipping
- Adds an outer
-
devenv/otel: update
devenvto the upstreamv2.1tag and move OTEL shell-entry notices ontodevenv.messages- Resolves OTEL mode, dashboard sync, and Grafana trace-link construction in a dedicated shell-entry task instead of ad-hoc
enterShelloutput - Auto-displays the OTEL shell-entry message through upstream task messages while keeping
otel-traceas a lightweight re-open helper - Scrubs ambient task trace context before emitting
devenv/shell:entryso the shell root span cannot self-parent or collide with laterdtroot spans - Emits
devenv/shell:entryvia the pinned store path forotel-spanso tracing still works beforeenterShellPATH mutations are fully visible
- Resolves OTEL mode, dashboard sync, and Grafana trace-link construction in a dedicated shell-entry task instead of ad-hoc
-
@overeng/genie: Validate GitHub Actions
runs-onlabels before emitting workflow YAML- Fails
geniewhen workflow jobs serialize non-string, empty, or stale placeholder runner labels likenull/...=undefined - Prevents CI helper API drift from silently generating invalid workflow files that only fail later in GitHub Actions
- Fails
-
@overeng/megarepo: Harden store against broken worktree remnants (#423)
hasWorktreenow checks for.gitfile existence instead of just directory existence, so broken partial worktrees are properly detected and recreated- Lock-protected worktree creation cleans up broken directory remnants and prunes stale git worktree bookkeeping before recreating
- Fix semaphore creation race in
StoreLockusingSynchronizedReffor atomic get-or-create
-
flake / nix/workspace-tools: Document and regression-test strict downstream reuse of effect-utils' canonical nixpkgs input
- Adds downstream flake-input and
devenvfixture coverage for standalone andrepos/effect-utils-prefixed consumers - Makes the intended contract explicit: downstream repos should follow
effect-utils/nixpkgsinstead of overriding effect-utils to their ambient nixpkgs
- Adds downstream flake-input and
-
@overeng/megarepo: Skip pre-flight hygiene checks in apply mode (#423)
- Apply mode self-heals all store issues (missing bare repos, broken worktrees, ref mismatches)
- Eliminates races in
--allmode where concurrent nested syncs modify shared store state while sibling pre-flight checks observe it - Simplifies
runPreflightChecksto lock-mode-only (removesmode/commitModeparameters and exception lists)
-
devenv/tasks/shared/nix-cli: Update multiple stale Nix FOD hashes per
dt nix:hash:*iteration- Adds
nix build --keep-goingto surface all fixed-output hash mismatches from one build - Parses and applies multiple reported hash updates in one pass instead of only the first mismatch
- Adds regression coverage for mixed main-hash and local-dependency hash updates
- Adds
-
nix/workspace-tools/mk-pnpm-deps / mk-pnpm-cli / oxc-config-plugin: Switch Nix-contained pnpm builds to precomputed relocatable install trees
- Prepares the staged workspace install tree once inside the fixed-output derivation instead of restoring a vendored pnpm store and rerunning
pnpm installin downstream builds - Normalizes pnpm's absolute-path and timestamp metadata so the prepared tree stays deterministic across repeated builds
- Restores the prepared tree into the real workspace and relocates pnpm path placeholders before Bun-based build steps run
- Prepares the staged workspace install tree once inside the fixed-output derivation instead of restoring a vendored pnpm store and rerunning
-
nix/workspace-tools/mk-pnpm-deps: Drop pnpm bookkeeping metadata from prepared install trees
- Removes
.modules.yamland.pnpm-workspace-state-v1.jsonfrom the archived prepared tree because downstream Nix builders restore the tree and go straight to Bun instead of rerunning pnpm - Eliminates the remaining runner-specific pnpm metadata nondeterminism that was still flipping prepared-tree hashes across CI environments
- Removes
-
nix/workspace-tools/mk-pnpm-cli: Keep
pnpmavailable in prepared-tree build environments- Restores
pnpmtonativeBuildInputsso downstream packages can keep usingpnpm exec ...inpostBuildhooks after the install tree is precomputed - Gives pnpm a writable HOME and disables package-manager self-bootstrap in the builder so
pnpm execremains sandbox-safe and does not try to install a different pnpm version under/homeless-shelter - Fixes downstream CLI packages with asset builds layered on top of
mkPnpmCli, such asop-proxyandfactory
- Restores
-
CI workflow / genie/ci-workflow: Evict cached pnpm-deps outputs before CI jobs resolve
oxlint-npm- Avoids stale fixed-output pnpm cache entries masking the validated prepared-install-tree hash on CI runners
- Applies the cache bust to each job that resolves the shared Nix toolchain so
nix-checkand the faster task jobs agree on the same fresh deps output
-
@overeng/genie: Fail
genie --checkwhen inherited peer deps use ranged local install versions- Allows ranged
peerDependencies - Requires explicit local install versions in
dependencies/devDependencies/optionalDependencies
- Allows ranged
-
@overeng/megarepo: Handle stale locked commits during
mr sync --pull- Prevents recursive sync from aborting when nested pinned members reference commits that no longer exist
- Allows
mr sync --pull --forceto recover pinned branch members by resolving the tracked ref head - Adds regression coverage for recursive
--pull --allwith nested stale pinned lock entries
-
devenv/lint: Adopt
execIfModifiednegation patterns and drop the obsolete full-workspace lint install dependency- Excludes vendored/generated trees like
node_modulesduring lint cache invalidation - Keeps
oxlintinstall-free by using the bundled Nix JS plugin instead of the source plugin path - Retains the package-local
genieinstall dependency becausegenie --checkstill runs via the repo's source-mode CLI
- Excludes vendored/generated trees like
-
devenv/tasks/shared/check.nix: Give aggregate check tasks explicit no-op commands so
devenv tasks run check:*actually traverses their dependencies- Prevents current
devenvfrom treatingcheck:quick/check:allas skippedNo commandwrappers - Restores the intended shared quick-check entrypoint for downstream repos
- Prevents current
-
Effect TypeScript tooling: Pin the exported
effect-tsgoflake input back to the last known-good upstream revision- Reverts the
tsgoflake lock refresh after confirmingEffect-TS/tsgo@df2eaaacurrently fails to build its owneffect-tsgopackage - Keeps downstream
devenvshells green until the upstream patch set catches up again
- Reverts the
-
nix/workspace-tools/mk-pnpm-cli: Build pnpm CLIs from filtered aggregate-root workspaces instead of package-level deploy closures
- Moves patched dependency path discovery out of Nix evaluation and into the staging derivation
- Preserves lockfile-driven patch staging for root and external install roots without recursive eval-time YAML walks
- Unblocks downstream composed flake evaluation that was previously overflowing in
parsePatchedDependencyPaths - Stages the target package and its workspace closure under one canonical root workspace
- Installs dependencies at that staged root with the same aggregate lockfile model used by local dev
- Compiles the target entrypoint with Bun from the staged package directory, reducing coupling to bespoke deploy-time workspace surgery
- Narrows pnpm deps fetching to the staged root lockfile, closure package manifests, and referenced patch files
- Removes legacy deploy-specific behavior and normalizes the store against the staged aggregate workspace input
- Keeps the smoke harness focused on real Nix builds of the
genieandmegarepopackages
-
@overeng/tui-react: Add
@types/reactand@types/react-reconcilerto peer dependencies- Consumers need these type packages to type-check the
.tsxsource exports
- Consumers need these type packages to type-check the
-
devenv/tasks/shared/vercel.nix: Export deploy URLs as task output env vars and fail fast when URL extraction fails
- Captures Vercel CLI output inside task execution and extracts the deployment URL deterministically
- Writes
VERCEL_DEPLOY_URLandVERCEL_DEPLOY_URL_<DEPLOYMENT_NAME>viaDEVENV_TASK_OUTPUT_FILE - Enables CI callers to consume deploy URLs from structured task output instead of brittle log scraping
- devenv/tasks/shared/ts-effect-lsp.nix: document the tracked future unification of the standalone Effect LSP task with
ts:check- Adds a linked TODO for collapsing the separate task once the main workspace TypeScript check becomes
Effect-TS/tsgo-backed
- Adds a linked TODO for collapsing the separate task once the main workspace TypeScript check becomes
- @overeng/genie: tighten pnpm workspace SSOT around package seeds
- Removes
extraPackagesfrompnpmWorkspaceYaml.root(...)and the matchingadditionalMemberPathsgraph helper escape hatch - Removes committed package-level
pnpm-workspace.yamlprojections in favor of internal build-time package closures - Removes
pnpmWorkspaceYaml.manual(...)andpackageJson.aggregate(...); all root projection now goes throughpnpmWorkspaceYaml.root(...)andpackageJson.aggregateFromPackages(...)with explicitrepoName - Adds
extraMembersas an exceptional escape hatch for non-genie-managed workspace members (e.g. standalone examples in livestore) — prefer real package generators overextraMemberswhenever possible - Stops
genie/external.tsfrom depending on internal workspace-graph helpers and documents the seed-only aggregate model
- Removes
- Effect TypeScript tooling: switch local language-service integration to Nix-provided
effect-tsgo- Repoints the dev environment to upstream
Effect-TS/tsgo - Renames generator helpers/comments to describe the current tsgo-based model
- Keeps the
@effect/language-servicetsconfig plugin entry only as the current upstream tsgo configuration channel
- Repoints the dev environment to upstream
- pnpm/dev workspace: Switch dev installs to a generated repo-root hoisted pnpm workspace
- Adds generated root
package.jsonandpnpm-workspace.yamlwith explicit workspace members - Makes
pnpm:installown the repo-root install state and keeps the repo-rootpnpm-lock.yamlas the only authoritative lockfile - Updates package-scoped task execution to use
pnpm execso Vitest, Storybook, and Vite resolve against the active workspace topology - Derives package closures for Nix/tooling at build time instead of committing package-level
pnpm-workspace.yamlfiles - Clarifies in the install spec that the current symlinked
repos/*Megarepo realization keeps imported members on a cross-repolink:boundary rather than making them aggregate-root workspace importers
- Adds generated root
- @overeng/utils: Make Storybook
viteFinaltyping opt-in generic for linked Vite workspaces- Keeps the default helper API free of foreign Vite types
- Lets consumers opt into their own local
viteconfig type when they need a typedviteFinalhook
- devenv/tasks/shared/vercel.nix: Switch to prebuilt deploy mode (
vercel pull->vercel build->vercel deploy --prebuilt)- Replaces direct
vercel deploy <dir>with local prebuilt workflow for deterministic deploys - Replaces
path/outputDirdeployment config withcwd(defaults to".") - Adds
vercel pullstep to fetch project settings and env for the target environment - Adds
vercel buildstep to produce.vercel/outputlocally before deploying
- Replaces direct
- @overeng/genie / @overeng/notion-cli: Source inherited install-time dependency versions from the Genie catalog instead of copied peer ranges
- Keeps
peerDependenciesranged for consumers - Makes the catalog the single source of truth for concrete local install versions
- Keeps
- devenv/tasks/shared/ts-effect-lsp.nix: add reusable
ts:effect-lsptsgo diagnostics task- Exports
effect-tsgofrom the flake package set for downstream devenv consumers - Keeps the task standalone so repos can opt into Effect diagnostics without conflating them with stylistic lint
- Exports
- @overeng/genie: Added
githubActionruntime generator for type-safeaction.ymlgeneration - docs/bun: Document the upstream nested-workspace
patchedDependenciesblocker and link the Bun issue - docs/bun: Note the Bun-only local workspace fork workaround for patched dependencies
- @overeng/effect-rpc-tanstack: Add custom fetch transport support to
layerClient- Allows SSR callers to reuse Effect's built-in
FetchHttpClientwith an injected fetch implementation - Adds
fetchFromWebHandler(...)for adapting colocated web handlers to fetch-compatible transport - Avoids app-local reimplementation of Effect HTTP request body/stream handling
- Allows SSR callers to reuse Effect's built-in
- docs/node-modules-install: Clarify the pnpm GVS requirement for single-instance JS/TS dependency identity and add install-performance requirements
- devenv/tasks/shared/ts.nix: remove the legacy
ts:patch-lsppatching flow from the shared TypeScript task module- Drops the
lspPatchCmd,lspPatchAfter, andlspPatchDirparameters from the exported shared task API - Removes stale shell-entry and OTEL references to
ts:patch-lsp
- Drops the
- devenv/tasks/shared/setup.nix: Remove
setup:opt:*wrapper tasks andsetup:optionalgate- Optional tasks now use native
@completedependency suffix instead of nesteddevenv tasks runwrappers - Eliminates 6x shell re-evaluation, ~5.9s trace gap, fork-bomb guards, and filesystem locks
- The workaround for
cachix/devenv#2480is no longer needed since we usedevenv shell(not direnv)
- Optional tasks now use native
- nix/workspace-tools: Remove compatibility-only Nix surface from CLI builders/tasks
- Drops the dead
packageJsonDepsHashargument from bothmk-pnpm-cliand exportedmk-bun-cli - Removes the deprecated
devenvModules.tasks.git-hooks-fixexport and deletes its module
- Drops the dead
- CI diagnostics: add temporary root-cause instrumentation for Nix store corruption flakes (
#272)validateNixStoreStepnow captures full verify/repair/devenv logs and runner fingerprint into a diagnostics directory- Failed jobs now add a compact diagnostics summary and upload a diagnostics artifact for triage
- Added a temporary
workflow_dispatchdebug switch to force a controlled CI failure and verify diagnostics summary/artifact behavior end-to-end - Marked as temporary with explicit cleanup intent once root cause is identified and CI is stable
- devenv/tasks/shared/ts.nix: Fix
ts:emitmissing--buildflagtscWithDiagnosticswas called without--build, causing tsc to treattsconfig.all.jsonas a source file- Previously masked by
setup:opt:*wrappers silently swallowing the failure
- beads packaging: Avoid long emulated builds by using patched prebuilt
bdrelease binaries (v0.55.4)nix/beads.nixnow fetches release tarballs instead of compiling Go sources under QEMU- Linux binaries are patched with Nix loader/RPATH (
icu74) so Dolt-enabledbdruns correctly
- @overeng/genie:
genie --checknow fails fast on fatal.genie.tsimport/build errors and marks interrupted sibling checks as canceled- Prevents indefinite stalls when a sibling check remains in-flight after a fatal import/build failure
- Final JSON/TUI failure state is reconciled from
GenieGenerationFailedError.filesto avoid staleactiveentries
- beads packaging/tasks: Fix
bdDolt startup failures on macOS by building with CGO enabled and updating beads task/hook invocations for current CLI flagsnix/beads.nixnow buildsbdfrom source (buildGo126Module) with CGO + ICU/SQLite inputs instead of prebuilt no-CGO release tarballsnix/devenv-modules/tasks/shared/beads.nixnow uses Dolt-directory bootstrap checks and removes deprecated--no-daemon/--no-dbflag usage
-
genie/ci-workflow: Switch CI helpers to lock-pinned
DEVENV_BINinstead of PATHdevenv- Replaced
installDevenvFromLockStepwithpreparePinnedDevenvStepand made task commands use"$DEVENV_BIN" -
validateNixStoreStepnow runsdevenv infowithrestrict-eval = falseappended inNIX_CONFIG -
runDevenvTasksBeforenow forwards that unrestrictedNIX_CONFIGto alldevenv tasks run ...calls -
standardCIEnvnow defaultsNIX_CONFIGtorestrict-eval = falsefor CI jobs, and validation/tasks use that shared default
- Replaced
-
@overeng/megarepo: Scope nested
megarepo.lockreconciliation to recursive sync mode-
mr syncnow syncs direct member lock artifacts only (flake.lock/devenv.lock) - Nested
megarepo.lockreconciliation now runs only withmr sync --all
-
-
devenv/tasks/shared/megarepo.nix: Make
megarepo:syncalways run with--frozen- Prevents shell-entry and routine task runs from rewriting
megarepo.lock - Adds
megarepo:sync:updatefor intentional non-frozen lockfile updates
- Prevents shell-entry and routine task runs from rewriting
-
devenv/dt: Remove CI/non-interactive TUI suppression workaround now that devenv auto-disables TUI in CI
- Dropped manual
DEVENV_TUI=falsehandling and PTY stderr piping fromdt - Updated failure re-run hints to use
devenv tasks run ... --mode beforewithout--no-tui
- Dropped manual
-
@overeng/genie: Reduce duplicate check-time work by reusing loaded genie modules between content verification and validation
- Added
loadGenieFile/checkFileDetailedin core generation to return reusable module/context metadata -
checkAllnow passes preloaded modules intorunGenieValidationinstead of re-importing every.genie.ts - Switched formatting hot path to in-process
oxfmtAPI with CLI fallback, eliminating per-file formatter process spawn in normal operation
- Added
-
devenv/otel-span: Consolidate
otel-spanandotel-emit-spaninto single CLI with subcommands-
otel-span run <service> <span-name> [opts] -- <cmd>replaces bareotel-span <service> ... -
otel-span emitreplacesotel-emit-span(reads OTLP JSON from stdin) - Breaking: subcommand is now required
-
-
devenv/otel.nix: Hard-cut system-mode dashboard sync compatibility
-
OTEL_MODE=systemnow fails shell entry whenOTEL_STATE_DIR,OTEL_EXPORTER_OTLP_ENDPOINT, orotelCLI is missing - Removed
OTEL_DASHBOARDS_DIRshell env export - Removed shell-side
extraDashboardsmerge logic;extraDashboardsis now rejected in system mode
-
-
devenv/otel.nix: Replace
curlwith file spool (otlpjsonfilereceiver) inotel-span- Spans are written to
$OTEL_SPAN_SPOOL_DIR/spans.jsonlinstead of HTTP POST - Collector picks up spans via
otlpjsonfilereceiver(500ms poll, delete after read) - Falls back to
curlif spool dir not available - Reduces per-span overhead from ~58ms to <1ms
- Spans are written to
-
devenv/otel-span: Emit boolean attributes and manage task trace context
--attrnow serializestrue/falsevalues asboolValue(aligns dashboard TraceQL filters)otel-spanreadsOTEL_TASK_TRACEPARENT(preferred overTRACEPARENT) and exports both for child processes- This isolates task traces from stale shell
TRACEPARENTvalues caused by devenv shell re-evaluations
-
devenv/dt: Simplify trace context propagation
dtnow clearsTRACEPARENTand delegates context management entirely tootel-span- Removes manual trace/span ID generation that was previously duplicated between
dt.nixandotel-span
-
devenv/otel-span: Add
--status-attr KEYflag for status check spans- Derives bool attribute from exit code (0=true, non-zero=false)
- Forces span status to OK (status checks aren't errors, exit 1 means "not cached")
- Used by
trace.statusto settask.cachedwithout masking the real exit code
-
devenv/tasks/lib/trace.nix: Trace status checks with method and sub-trace support
trace.statusnow accepts amethodparameter ("binary","hash","path")- Status body runs INSIDE
otel-span(not post-hoc) so sub-programs inherit TRACEPARENT - Binary status checks (e.g.
genie --check,mr status) now produce child spans task.cachedis derived from exit code via--status-attr(no explicit bool passing)- Each real status execution gets its own span (no deduplication — duplicate spans from devenv's shell re-evaluations accurately reflect what actually happened)
-
devenv/tasks/shared/lint-oxc.nix: Wire up
genieCoverageExcludesand addgenieCoverageFiles(#198)genieCoverageExcludeswas accepted but never applied; now uses git pathspec exclusion- New
genieCoverageFilesparameter (default:["package.json" "tsconfig.json"]) makes checked file types configurable - Removed dead
defaultExcludes/excludeArgscode from obsoletefind-based approach - Made doc examples more generic (removed
@overeng-specific paths)
-
genie: Add programmatic TS SDK (
@overeng/genie/sdk) for calling genie's generate/check logic from TypeScript without the CLI. Core orchestration extracted into sharedcore.tsusing PubSub + Stream event bus pattern, consumed by both CLI (TUI progress) and SDK (silent). -
genie: Split
src/build/intosrc/core/(shared, no TUI deps),src/build/(CLI/TUI), andsrc/sdk/(programmatic API). Each export path now maps 1:1 to a directory. SDK consumers no longer needjsxin their tsconfig. -
devenv/tasks/shared/worktree-guard.nix: Git hook to enforce worktree workflow
- Refuses commits on the default branch (detected via
refs/remotes/<remote>/HEADwith fallback) - Optionally refuses commits from the primary worktree
- Detects megarepo store worktrees and prevents commits when the path-implied ref doesn't match
HEAD
- Refuses commits on the default branch (detected via
-
devenv/tasks/shared/ts.nix: Add
ts:emittask (tsc --build --noCheck) and use it for shell entry- Keeps
ts:buildas the typechecked build - Improves shell entry performance by skipping full type checking during emit
- Shell entry now runs
ts:patch-lspseparately;ts:emitno longer depends on patching so it can be used standalone
- Keeps
-
devenv/otel.nix: TRACEPARENT propagation for shell entry waterfall tracing
setup:gategenerates root TRACEPARENT for shell entry tracessetup:save-hashemits adevenv:shell:entryroot spantrace.nixgenerates fallback TRACEPARENT when not already set- Enables end-to-end waterfall view in Grafana Tempo for shell startup
-
devenv.nix: Nix eval visibility and cold-start detection
SHELL_ENTRY_TIME_NScaptured at enterShell startshell:readymarker span withcold_startattributedt.nixaddsshell.ready_msattribute for eval+setup time tracking
-
devenv/otel.nix:
otel:testtask for shell-level unit tests- Validates JSON format, TRACEPARENT propagation, spool write, and fallback
- Runs offline (~2s) without requiring
devenv up
-
@overeng/otel-cli: Spool file verification in
otel debug test- Tests both HTTP and file spool delivery paths end-to-end
- Gracefully skips spool test when
OTEL_SPAN_SPOOL_DIRnot set
-
@overeng/genie: Validate
pnpmWorkspaceYamlrejects absolute paths inpackagesduringgenie:check(#152) -
@overeng/otel-cli: New Effect CLI package for OTEL stack diagnostics and trace exploration
otel health— per-component health status (Grafana, Tempo, Collector)otel trace ls— tabular trace listing with TraceQL query filteringotel trace inspect— span tree with ASCII waterfall visualizationotel metrics ls/query/tags— TraceQL metrics querying with sparkline renderingotel api— raw HTTP calls to Grafana, Tempo, Collector APIsotel debug test/dashboards— E2E smoke tests and dashboard inspection
-
devenv/otel.nix: Full OTEL observability stack as reusable devenv module
- OTEL Collector + Grafana Tempo + Grafana with hash-based deterministic port allocation
- Auto-provisioned dashboards via Grafonnet (Jsonnet DSL) build pipeline
otel-spanshell helper for wrapping commands in OTLP trace spans- Compatible with Effect OTEL layers (same env var/protocol)
-
devenv/tasks/lib/trace.nix: Task tracing helper with cache status tracking
trace.execwraps task exec scripts withotel-spanfor child span emissiontrace.statusemits spans withtask.cached=truefor cached tasks- Applied to all shared task modules (ts, genie, lint, pnpm, test, megarepo, etc.)
-
devenv/otel/dashboards: 6 Grafonnet dashboards with manual grid positioning
- Overview, dt Task Performance, Shell Entry, pnpm Install Deep-Dive, TS App Traces, dt Duration Trends
- dt-tasks dashboard with cache status filtering (executed vs cached)
-
@overeng/utils:
node/otelmodule for Effect-native OTEL instrumentation in CLI appsmakeOtelCliLayer()wires OTLP exporter with W3C TRACEPARENT propagation fromdttasks- Zero overhead when
OTEL_EXPORTER_OTLP_ENDPOINTis not set
-
devenv: Netlify deploy tasks for storybook preview deployments
- New shared
netlify.nixtask module withnetlify:deploy:<name>per-package tasks - Supports prod, PR preview (alias), and local draft deploy modes via
--inputflags - CI job
deploy-storybooksdeploys all 7 storybooks on PRs and pushes to main - Replaces Vercel-based storybook deployments
- New shared
-
@overeng/tui-react: Standalone
runfunction with dual (data-first/data-last) API (#129)run(app, handler, { view })replacesEffect.scoped(Effect.gen(function* () { const tui = yield* app.run(view); ... }))- Scope managed internally — consumers no longer need
Effect.scoped - Error type
Einferred from handler (no explicit error schema needed) - Added
TuiAppTypeIdbrand andisTuiApppredicate for runtime type detection
-
devenv/lint: Simplify
lint:check:formatby reverting to directoxfmt --checkinvocation (#157)- Removed
git ls-filescomplexity — oxfmt's directory walker already excludesnode_modules - Added
pnpm:installdependency to ensure stablenode_modulesstate during formatting - Investigation confirmed
experimentalSortImportsuses string-based classification (no filesystem reads)
- Removed
-
@effect/language-service/TypeScript config: Elevate
missedPipeableOpportunitydiagnostics to warningsmissedPipeableOpportunitynow emits aswarningso Effect LSP findings are visible in non-IDE CLI typechecks- Keeps
ts:checkin--noEmitmode while preserving the existingts:check/ts:buildbehavior split (#218)
-
CI/storybook: Fix storybook builds used by Netlify preview deploys
- Stub
@opentui/*in@overeng/genieStorybook build (OpenTUI requires Bun runtime) - Fix
@overeng/tui-reactexamples importingsrc/mod.ts(actual entry issrc/mod.tsx)
- Stub
-
CI/deploy-storybooks: Make Netlify preview deploys more reliable
- Run the deploy job on
ubuntu-latest(avoids flaky Namespace runner Nix store state) netlify:deploy:*now depends onstorybook:build:*so deploys always have build output
- Run the deploy job on
-
@overeng/tui-react: Fix
OutputCauseSchemausingSchema.Neverfor error field (#129)- Changed
error: Schema.Nevertoerror: Schema.DefectinOutputCauseSchema - Previously, typed errors (e.g.
GenieGenerationFailedError) causedParseError: Expected neverduring JSON encoding, masking the real error
- Changed
-
devenv/tasks: Optimized
check:quickby prioritizing utils package install- Moved utils and utils-dev to front of install queue for earlier
ts:patch-lspstart check:quickimproved from ~18-28s to ~14-15s through better task parallelism
- Moved utils and utils-dev to front of install queue for earlier
-
devenv/ts.nix: Per-project tsc tracing via
--extendedDiagnosticsparsing- When OTEL is available,
ts:checkandts:buildemit per-project child spans with timing attributes - ~3% overhead when active, zero overhead when OTEL unavailable
- Renamed
ts:watchtots:build-watch
- When OTEL is available,
-
@overeng/tui-react, @overeng/megarepo, @overeng/notion-cli, @overeng/genie: Migrated all consumers to standalone
runAPI (#129)- All command files now use
run(App, handler, { view })instead of manualEffect.scoped+app.run() - Updated test utilities (
runTestCommand) to not requireScope.Scope
- All command files now use
-
@overeng/tui-react: Automatic log capture for progressive-visual modes (breaking change)
outputModeLayer()now captures all Effect logs andconsole.*output in tty/ci/alt-screen modes- Captured logs accessible via
useCapturedLogs()hook in React components - Prevents accidental log output from corrupting TUI terminal rendering
- Console methods (
log,error,warn,info,debug) are scoped and restored on cleanup - New
LogCapture.tsmodule withcreateLogCapture(),CapturedLogsProvider,useCapturedLogs() - New example
06-log-capture/demonstrating the feature - Updated spec.md with log capture documentation
-
@overeng/utils-dev: New package with enhanced Vitest utilities for Effect-based testing
makeWithTestCtx/withTestCtxfor automatic layer provisioning, OTEL integration, and timeoutsasPropfor property-based testing with shrinking phase visibility- Migrated 22 test files across 7 packages to the new pattern
-
@overeng/genie: GitHub Repository Ruleset generator (
githubRuleset)- Type-safe configuration for GitHub Repository Rulesets via the REST API
- Full support for all 22 rule types with comprehensive JSDoc documentation
- Generates JSON config applied via
gh api repos/{owner}/{repo}/rulesets - Added ruleset configuration for effect-utils protecting the main branch
-
devenv/ts.nix: Centralized Effect Language Service patching via
ts:patch-lsptask- Removed per-package
postinstall: 'effect-language-service patch'scripts from all 15 packages - Added
lspPatchCmdparameter tots.nixthat creates ats:patch-lsptask - Fixes consumer install failures for published packages (e.g.
@overeng/react-inspector)
- Removed per-package
-
Effect LSP patching now runs automatically before
ts:check,ts:build-watch,ts:build -
devenv/ts.nix: Use package-local patched tsc binary for Effect Language Service diagnostics
- Added
tscBinparameter (default:"tsc") to specify a patched TypeScript binary - Nix-provided tsc is unpatched and silently skips Effect plugin diagnostics
ts:cleanuses Nix tsc (always available, doesn't need the patch)
- Added
-
@overeng/megarepo, @overeng/tui-react: Migrated tests from async/await to
@effect/vitest- All Effect-based tests now use
it.effect()pattern instead ofasync () => { await Effect.runPromise(...) } - Provides better stack traces, fiber-aware timeouts, and cleaner Effect integration
- See #92
- All Effect-based tests now use
-
@overeng/megarepo: Simplified nix integration - removed workspace generator
- Removed
mr generate nixcommand and.envrc.generated.megarepofile - Removed
.direnv/megarepo-nix/workspacemirror directory - Removed
MEGAREPO_ROOT_*,MEGAREPO_MEMBERS,MEGAREPO_NIX_WORKSPACEenv vars - Use
DEVENV_ROOT(provided by devenv) instead ofMEGAREPO_ROOT_NEAREST - Simplified
.envrcto justuse devenv(no generated file needed)
- Removed
-
@overeng/megarepo: Split
pnpmDepsHashby platform to fix Linux/Darwin store divergence -
@overeng/megarepo: Nix lock sync is now auto-detected and uses top-level config
- Breaking: Moved from
generators.nix.lockSyncto top-levellockSyncconfig - Lock sync is now auto-detected: enabled if
devenv.lockorflake.lockexists in megarepo root - No configuration needed for the common case; set
lockSync.enabled: falseto opt-out - Removed vestigial
NixGeneratorConfigandgenerators.nixconfig options
- Breaking: Moved from
-
nix/devenv-modules/tasks/shared/megarepo.nix: Simplified megarepo tasks
- Removed
megarepo:generatetask (no longer needed) - Simplified
megarepo:checkto just verify repos/ directory exists - Tasks no longer check for
.envrc.generated.megarepoor workspace flake
- Removed
-
@overeng/megarepo: Configure fetch refspec when cloning bare repos (#111)
-
git clone --baredoesn't setremote.origin.fetch, breakinggit push --force-with-lease - Now
cloneBareconfigures+refs/heads/*:refs/remotes/origin/*after clone - Ensures remote tracking refs are created on fetch for proper git workflows
-
-
@overeng/tui-react: Strengthen JSON schema typing in
TuiAppunit tests- Replaced generic JSON parsing and
anycasts with schema-encoded helpers
- Replaced generic JSON parsing and
-
@overeng/genie: Fix YAML serializer producing empty output with matrix strategy
- When GitHub Actions workflows use
${{ }}expressions inside inline arrays (e.g.,runs-on: [${{ matrix.runner }}]), oxfmt fails to parse the YAML - The
formatWithOxfmtfunction now returns original content when oxfmt produces empty output - Closes #108
- When GitHub Actions workflows use
-
nix/devenv-modules/tasks/shared/test.nix: Self-contained test tasks - each package uses its own vitest
- Previously test tasks shared a vitest binary from
@overeng/utils, violating self-contained packages requirements (R1-R5) - Now each package runs tests using
node_modules/.bin/vitestfrom its own dependencies - Added
vitest.config.tsto packages that were missing one: effect-path, effect-rpc-tanstack, genie, notion-cli, notion-effect-client, notion-effect-schema - Removed deprecated
vitestBin,vitestConfig, andvitestInstallTaskparameters from test module - This ensures packages are independently testable without cross-package dependencies
- Previously test tasks shared a vitest binary from
-
nix/devenv-modules/tasks/shared/nix-cli.nix: Preflight lockfile/package.json fingerprint checks in
nix:check- Prevents warmed Nix stores from masking stale hashes
- Makes
nix:checkdeterministic in CI vs local runs (R5)
-
nix/workspace-tools/lib/mk-pnpm-cli.nix: Deterministic pnpm store tarball creation
- Normalizes tar output (stable ordering + fixed timestamps)
- Strips non-deterministic pnpm store
checkedAtmetadata - Prevents pnpm deps hash churn across CI runs
-
nix/workspace-tools/lib/mk-pnpm-cli.nix: Force
supportedArchitecturesin Nix pnpm installs- Ensures pnpm store hashes remain stable across macOS/Linux (R5)
-
nix/workspace-tools/lib/mk-pnpm-cli.nix: Generate pnpm store with recursive install
- Aligns store generation scope with offline install (R6)
- Prevents missing tarballs during
nix:checkfor multi-package workspaces
-
nix/workspace-tools/lib/mk-pnpm-cli.nix: Force dev dependencies during pnpm store generation
- Avoids production-only installs that drop dev-only tarballs
- Fixes
ERR_PNPM_NO_OFFLINE_TARBALLinnix build/nix:check
- @overeng/mono: Removed package entirely — all functionality is now covered by devenv tasks (
dt). The package had zero consumers across all repos.
-
pnpm workspaces: Hoist React-family packages in React-enabled workspaces to prevent duplicate React instances during local dev
-
nix/workspace-tools/lib/mk-pnpm-cli.nix: Added
packageJsonDepsHashparameter to fix build failuresbuild.nixfiles were passingpackageJsonDepsHashbut the function didn't accept it- Fixes
nix flake checkfailures and downstream repo devenv shell issues - Renamed from
depsHashtopackageJsonDepsHashfor clarity (breaking change)
-
nix/workspace-tools/lib/mk-bun-cli.nix: Added
lockfileHashandpackageJsonDepsHashparameters for consistency- Both CLI builders now support the same fingerprint hash interface
- Enables
nix:check:quickto work uniformly across both build types
-
nix/devenv-modules/tasks/shared/nix-cli.nix: Fixed missing task dependencies and improved error messages
nix:check:*tasks now depend onpnpm:install(full workspace)- Previously only depended on per-package install, causing failures when other packages had stale lockfiles
- Added clear error messages for stale lockfiles with actionable fix instructions
- Detects
ERR_PNPM_OUTDATED_LOCKFILEand suggestsdt pnpm:update && dt nix:hash
-
nix/devenv-modules/tasks/shared/pnpm.nix: Added
pnpm:updatetask- Runs
pnpm install --no-frozen-lockfilein all packages to update lockfiles - Use when adding new dependencies that cause
ERR_PNPM_OUTDATED_LOCKFILEerrors - Now depends on
genie:runso generated package.json files are up to date
- Runs
-
nix/devenv-modules/tasks/shared/pnpm.nix: Renamed
pnpm:clean-lock-filestopnpm:reset-lock-files- Makes it clear this is a destructive, last-resort operation
-
nix/devenv-modules/tasks/shared/check.nix: Updated check task semantics
check:quick- Fast development checks (genie, typecheck, lint, nix-fingerprint only)check:all- Comprehensive validation including fullnix flake checkcheck:packages- New task to validate allPackages matches filesystem
-
nix/devenv-modules/tasks/local/workspace-check.nix: New local validation task
- Validates that
allPackagesin devenv.nix matches actual filesystem packages - Prevents Nix build failures from unmanaged packages with stale lockfiles
- Located in
local/directory (effect-utils specific, not for reuse)
- Validates that
-
nix/devenv-modules/tasks: Reorganized into
shared/andlocal/directoriesshared/- Reusable tasks meant for other repos via flake inputlocal/- Effect-utils specific tasks (not exported in flake.nix)- Added README.md documenting the organization
-
nix/devenv-modules/tasks/shared/check.nix: Added
extraChecksparameter- Allows repos to inject additional check tasks (e.g.,
workspace:check) - Maintains reusability while enabling local customization
- Allows repos to inject additional check tasks (e.g.,
-
devenv.nix: Updated taskModules to use
shared/directory paths- Fixed regression where local paths weren't updated after directory restructure
-
devenv.nix: Added missing
packages/@overeng/tui-reacttoallPackages
- genie/internal: Ensure
pnpmWorkspaceYamlis locally imported sopnpmWorkspaceReactdoes not throw a ReferenceError
- @overeng/effect-rpc-tanstack: New package for Effect RPC integration with TanStack Start
createRpcHandler- Create server function handlers from Effect handlerscreateRpcHandlerWithLayer- Handler with Effect Layer dependency injectionwrapHandler- Wrap handlers for proper error handlingrpcValidator- Schema validator for TanStack Start server functionsRpcRequest/RpcResponse/RpcSuccess/RpcFailure/RpcDefect- Protocol typesRpcDefectError- Client-side error type for unexpected server errors- Basic example with TanStack Start app and Playwright tests
-
@overeng/utils: Updated
effect-distributed-lockto 0.0.11 and patched root exports to avoid loading optionalioredis(see ethanniser/effect-distributed-lock#10) -
@overeng/notion-effect-cli: Migrated config from JSON to TypeScript (breaking change)
- Config file is now
notion-schema-gen.config.tsinstead of.notion-schema-gen.json - Databases are now keyed by their Notion ID instead of an array
- New
defineConfighelper with full type checking and autocompletion - New typed
transformshelpers (e.g.,transforms.status.asString) instead of string literals - New
outputDiroption for base output directory (paths are relative to it) - Import config helpers from
@overeng/notion-effect-cli/config - CLI now requires Bun runtime for native TypeScript config loading
- Config file is now
-
@overeng/notion-effect-cli: Adopted type-safe file paths from
@overeng/effect-path(breaking change)DatabaseConfig.outputnow requiresRelativeFilePath- usefile()helperSchemaGenConfig.outputDirnow requiresRelativeDirPath- usedir()helper- Import
fileanddirhelpers from@overeng/notion-effect-cli/config - Internal path operations now use
EffectPath.ops.*instead ofnode:path - Removed
Path.Pathservice dependency from Effect requirements
-
Monorepo CLI: Replaced Biome with oxc toolchain (oxlint + oxfmt)
- Removed
@biomejs/biomedependency mono lintnow uses oxlint exclusivelymono fmt [--check]- Format code with oxfmt (Prettier-compatible, 30× faster)mono checknow includes format verification- Added shared oxlint/oxfmt configuration via
@overeng/oxc-configpackage
- Removed
-
@overeng/oxc-config: New package for shared oxlint + oxfmt configuration
- Base config with sensible defaults for TypeScript/Effect projects
- Rules:
import/no-dynamic-require(warn),oxc/no-barrel-file(warn, exceptmod.ts),overeng/named-args(warn),import/no-commonjs(error),import/no-cycle(warn),func-style(warn, prefer expressions/arrows) - Re-exports only allowed from
mod.tsentry point files - Custom
overeng/named-argsrule enforces named arguments pattern (options objects), with automatic exemptions for callbacks, rest params, and Effect patterns
-
@overeng/utils: Force revoke / lock stealing for file-system semaphore backing
forceRevoke(options, key, holderId)- Forcibly revoke a specific holder's permitsforceRevokeAll(options, key)- Revoke all holders for a semaphore keylistHolders(options, key)- List active holders with permit counts and expiry timesHolderInfotype for holder informationHolderNotFoundErrorfor when target holder doesn't exist- See upstream feature request: ethanniser/effect-distributed-lock#9
-
@overeng/notion-effect-schema: New
PropertySchemadiscriminated union for typed database property definitions- Full support for all 23 Notion property types using
Schema.TaggedStruct SelectOptionConfig,StatusGroupConfigfor select/multi-select/status optionsNumberFormat,RollupFunctionenums- All property schemas exported individually (e.g.,
SelectPropertySchema,RelationPropertySchema)
- Full support for all 23 Notion property types using
-
@overeng/notion-effect-client: New
SchemaHelpersmodule for database schema introspectiongetProperties({ schema })- Get all properties as typedPropertySchema[]getProperty({ schema, name })- Get single property by namegetPropertyByTag({ schema, name, tag })- Get property filtered by typegetSelectOptions({ schema, property })- Get select property optionsgetMultiSelectOptions({ schema, property })- Get multi-select optionsgetStatusOptions({ schema, property })- Get status optionsgetAnySelectOptions({ schema, property })- Get options from any select-like propertygetRelationTarget({ schema, property })- Get relation target database infogetFormulaExpression({ schema, property })- Get formula expressiongetNumberFormat({ schema, property })- Get number formatgetRollupConfig({ schema, property })- Get rollup configurationgetUniqueIdPrefix({ schema, property })- Get unique ID prefix
-
@overeng/notion-effect-schema: Renamed
DatabasetoDatabaseSchemafor clarity (breaking change)- The type represents the schema/structure of a database, not the data itself
-
@overeng/notion-effect-cli: Refactored introspect.ts to use new typed
PropertySchemafrom schema package- Removed manual property type definitions in favor of shared schemas
-
@overeng/notion-effect-cli: Generated schemas now include Effect Schema annotations
- Schemas include
identifieranddescriptionannotations for better debugging/tooling - Property fields with descriptions now have JSDoc comments instead of inline comments
- Typed options (when
--typed-optionsis used) also includeidentifierannotations
- Schemas include
-
Renamed @overeng/notion-effect-schema-gen to @overeng/notion-effect-cli to support more general-purpose CLI functionality
- Binary name changed from
notion-effect-schema-gentonotion-effect-cli - All commands remain the same:
generate,introspect,generate-config,diff
- Binary name changed from
-
@overeng/utils: Workspace helpers (
CurrentWorkingDirectory,EffectUtilsWorkspace) and command utilities (cmd,cmdText) with optional log capture/retention -
Monorepo CLI: Added
monoCLI for streamlined development workflowmono build- Build all packagesmono test [--unit|--integration] [--watch]- Run tests with filtering optionsmono lint [--fix]- Check formatting and run oxlintmono ts [--watch] [--clean]- TypeScript type checkingmono clean- Remove build artifactsmono check- Run all checks (ts + fmt + lint + test)- Available directly in PATH via
scripts/bin/monowrapper - VSCode tasks.json for easy command palette integration
- CI-aware output with GitHub Actions log grouping
-
@overeng/notion-effect-client: Block helpers and Markdown converter improvements
BlockHelpersnamespace with typed utilities for custom transformers:getRichText(block)- Extract rich text contentgetCaption(block)- Get media block captionsgetUrl(block)- Get URL from image/video/file/embed/bookmark blocksisTodoChecked(block)- Check to-do statusgetCodeLanguage(block)- Get code block languagegetCalloutIcon(block)- Get callout emojigetChildPageTitle(block)/getChildDatabaseTitle(block)- Get titlesgetTableRowCells(block)- Get table row cellsgetEquationExpression(block)- Get equation expression
BlockWithDatatype for blocks with type-specific data- All helpers also exported as standalone functions
- Rich Text utilities:
toPlainText,toMarkdown,toHtmlviaRichTextUtils - Recursive block fetching:
NotionBlocks.retrieveAllNested(flat stream),NotionBlocks.retrieveAsTree(tree) - Markdown converter:
NotionMarkdown.pageToMarkdown,NotionMarkdown.treeToMarkdown,NotionMarkdown.blocksToMarkdown - Custom transformer support for all 27 block types
-
@overeng/react-inspector: Added as git submodule for Effect Schema-aware data inspection
- DevTools-style object/table/DOM inspectors for React
- Enriched display of Effect Schema types with type names and custom formatting
- Runs on port 9001 (separate from effect-schema-form-aria Storybook on 6006)
- Maintains its own tooling (tsup, ESLint) - excluded from monorepo biome config
- @overeng/notion-effect-cli: Added comprehensive README with usage examples for CLI and programmatic API
-
@overeng/notion-effect-cli:
diffcommand for detecting schema drift- Compares current Notion database schema against an existing generated TypeScript file
- Reports added properties (new in Notion), removed properties (no longer in Notion), and type changes
--file/-f: Path to existing generated schema file (required)--exit-code: Exit with code 1 if differences found (useful for CI)- Parses generated schema files to extract property definitions
- Displays formatted diff output with summary
-
@overeng/notion-effect-client: Schema-aware typed queries and page retrieval
TypedPage<T>interface combining page metadata with decoded propertiesPageDecodeErrorfor schema decoding failuresNotionDatabases.query(): Now accepts optionalschemaparameter for typed resultsNotionDatabases.queryStream(): Now accepts optionalschemaparameter for typed streamingNotionPages.retrieve(): Now accepts optionalschemaparameter for typed retrieval- All methods return
TypedPage<T>when schema is provided, withid,createdTime,url,properties, and_rawaccess
-
@overeng/notion-effect-cli: Database API wrapper generation
--include-api/-aflag: Generate typed database API wrapper alongside schema- Generated API file includes:
query(): Stream-based query with auto-paginationqueryAll(): Collect all resultsget(): Retrieve single page by IDcreate(): Create page (when--include-writeenabled)update(): Update page (when--include-writeenabled)archive(): Archive page
- Config file support:
includeApioption in database and defaults config - API file written to
{output}.api.ts(e.g.,tasks.ts→tasks.api.ts)
-
@overeng/notion-effect-schema: Fixed
BlockSchemato preserve type-specific properties- Block objects now correctly retain their type-specific data (e.g.,
block.paragraph,block.heading_1) - Previously, decoding would strip these properties, breaking markdown conversion and block helpers
- Block objects now correctly retain their type-specific data (e.g.,
-
@overeng/notion-effect-client: Removed yieldable-error
Effect.failusage and simplified search result literal schema -
@overeng/notion-effect-cli: Replaced global
Errorfailures with tagged config/token errors -
@overeng/notion-effect-cli: Critical fixes to generated schema code
- Fixed import references to use correct transform namespaces (e.g.,
Title,Select,Numinstead ofTitleProperty,SelectProperty,NumberProperty) - Fixed write schema generation to use nested Write APIs (e.g.,
Title.Write.fromStringinstead ofTitleWriteFromString) - Generated schemas now correctly work with
@overeng/notion-effect-schemapackage - Added integration tests verifying generated schemas decode/encode properly with actual Notion API data structures
- Added runtime validation helpers to generated code:
- Read helpers:
decode{Name}Properties,decode{Name}PropertiesEffect - Write helpers:
decode{Name}Write,decode{Name}WriteEffect,encode{Name}Write,encode{Name}WriteEffect
- Read helpers:
- Fixed import references to use correct transform namespaces (e.g.,
- Renamed all packages from
@schicklingscope to@overengscope - TypeScript builds now emit ESM JavaScript to
dist/with source maps and declaration maps. - Property "read" transforms are now decode-only; write payloads are modeled separately via
*Writeschemas / transforms. - Notion HTTP client retry behavior:
- Treats request-body JSON encoding failures as typed
NotionApiError(instead of defects). - Respects
retry-afteron 429 responses when retrying.
- Treats request-body JSON encoding failures as typed
- Updated dependencies to latest versions (effect ^3.19.13, @effect/platform ^0.94.0)
- Moved all dependencies to pnpm catalog for centralized version management
- Updated pnpm catalog versions (Effect 3.19.14, @effect/platform 0.94.1, TypeScript 5.9.3, Vite 7.3.0, Vitest 3.2.4, Tailwind 4.1.18) and added @effect/rpc for peer compatibility
-
@overeng/effect-react: React integration for Effect runtime
makeReactAppLayerfor layer-based app initialization with ReactuseServiceContexthook for accessing Effect services from React componentsLoadingStatecontext for tracking app initialization progressServiceContextutilities for running effects with a provided runtime- React hooks:
useAsyncEffectUnsafe,useInterval,useStateRefWithReactiveInput cuidandslugutilities for generating unique IDs
-
@overeng/effect-schema-form: Headless form component for Effect Schemas
- Schema introspection utilities (
analyzeSchema,getStructProperties,analyzeTaggedStruct) - Field type detection: string, number, boolean, literal, struct, unknown
- Context + hooks API pattern for custom rendering
SchemaFormProviderfor design system integrationuseSchemaFormhook for building custom form UIs- Support for optional fields, tagged structs, and literal unions
formatLiteralLabelutility for human-readable label formatting
- Schema introspection utilities (
-
@overeng/effect-schema-form-aria: Styled React Aria implementation
- Pre-configured
AriaSchemaFormcomponent with accessible UI ariaRenderersobject for use withSchemaFormProvider- Individual styled components:
TextField,NumberField,BooleanField,LiteralField FieldGroupandFieldWrapperlayout components- Tailwind CSS styling with design token support
- Automatic segmented control/select switching for literal fields
- Pre-configured
-
@overeng/notion-effect-cli: Full CLI implementation for schema generation
generatesubcommand: Introspects a Notion database and generates Effect schemas--output/-o: Output file path for generated schema--name/-n: Custom name for the generated schema (defaults to database title)--token/-t: Notion API token (defaults to NOTION_API_TOKEN env var)--transform: Per-property transform configuration (e.g.,Status=raw)--dry-run/-d: Preview generated code without writing to file--include-write/-w: Include Write schemas for creating/updating pages--typed-options: Generate typed literal unions for select/status options
introspectsubcommand: Displays database schema informationgenerate-configsubcommand: Generates schemas for all databases from config- Config file support (
.notion-schema-gen.json) for multi-database projects - Configurable property transforms per type (raw, asString, asOption, asNumber, etc.)
- Support for all 21 Notion property types with sensible defaults
- Improved PascalCase handling that preserves existing casing
- Auto-formatting with Biome when available
- Uses Effect FileSystem and Path for file operations
- Generated code includes proper Effect Schema imports and type exports
- Deterministic code generation (no timestamps); header includes generator version
- Comprehensive unit tests for code generation functionality
-
@overeng/notion-effect-schema: Core Notion object schemas
Database,Page,Blockwith full field definitions- Parent types:
DatabaseParent,PageParent,BlockParent - File objects:
ExternalFile,NotionFile,FileObject - Icon types:
EmojiIcon,CustomEmojiIcon,Icon - Block type enum covering all 27 Notion block types
DataSourcefor database data sources
-
@overeng/notion-effect-schema: Comprehensive Effect schemas
- Foundation schemas:
NotionUUID,ISO8601DateTime,NotionColor,SelectColor - Rich text support:
RichText,TextAnnotations,MentionRichText,EquationRichText - User schemas:
Person,Bot,PartialUser,Userunion - Property schemas with:
- decode transforms (e.g.
Title.asString,Num.asNumber,Select.asStringRequired) - write payload schemas/transforms for page create/update (e.g.
TitleWrite,SelectWrite,PeopleWrite)
- decode transforms (e.g.
- Custom
docsPathannotation linking each schema to official Notion API docs - Proper Effect
Optionhandling for nullable/optional fields
- Foundation schemas:
-
@overeng/notion-effect-client: Comprehensive test suite with real API integration
- Unit tests for internal HTTP utilities
parseRateLimitHeaders,buildRequest,get,postfunctionsNotionApiError.isRetryablelogic- Pagination utilities:
paginationParams,toPaginatedResult,paginatedStream
- Integration tests for service modules (skipped when no token)
- Databases:
retrieve,query,queryStreamwith filters and pagination - Pages:
retrieve,create,update,archive - Blocks:
retrieve,retrieveChildren,retrieveChildrenStream,append,update,delete - Users:
me,list,listStream,retrieve - Search:
search,searchStreamwith filters and sorting
- Databases:
describe.skipIfpattern for graceful skipping when no API token- Separate
test:unitandtest:integrationnpm scripts
- Unit tests for internal HTTP utilities
Initial release of effect-notion monorepo.
- @overeng/notion-effect-schema: Effect schemas for the Notion HTTP API
- @overeng/notion-effect-client: Effect-native HTTP client for the Notion API
- @overeng/notion-effect-cli: CLI tool for schema generation
- Initial monorepo setup with pnpm workspaces
- TypeScript configuration with project references
- Modern ESM-first package structure