From 0796fb2c9f65283dd92e598630f67aeabe77e008 Mon Sep 17 00:00:00 2001 From: Phenix Rizen Date: Thu, 27 Aug 2026 06:53:05 -0500 Subject: [PATCH 1/4] docs: declare the maintained fork and add contributor governance This repository is a maintained fork of gorules/zen. Nothing said so, and the inherited README carried upstream's "we can't accept code contributions" policy, which is the opposite of this fork's. Adds what a fork needs and inherited none of: CONTRIBUTING.md, SECURITY.md, CODEOWNERS, a pull request template, issue forms, and a code of conduct. Guards every publish job against running on a fork. The inherited workflows push to crates.io, npm, PyPI, NuGet, and the gorules/zen-go repository under upstream's package names, and release-please cuts the tags that drive them. A fork must build and test but never publish, so each is now conditioned on the canonical repository. Co-Authored-By: Claude Opus 5 (1M context) --- .github/CODEOWNERS | 2 + .github/ISSUE_TEMPLATE/bug_report.yml | 83 ++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 11 +++ .github/ISSUE_TEMPLATE/feature_request.yml | 32 +++++++ .github/SECURITY.md | 40 +++++++++ .github/pull_request_template.md | 28 +++++++ .github/workflows/go.yaml | 4 +- .github/workflows/node.yaml | 2 +- .github/workflows/python.yaml | 2 +- .github/workflows/release-please.yaml | 3 + .github/workflows/rust.yaml | 2 +- .github/workflows/uniffi.yaml | 12 +-- CODE_OF_CONDUCT.md | 59 +++++++++++++ CONTRIBUTING.md | 97 ++++++++++++++++++++++ README.md | 47 ++++++++++- 15 files changed, 413 insertions(+), 11 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/SECURITY.md create mode 100644 .github/pull_request_template.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..151dbc5f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owner for everything in this repository. +* @phenixrizen diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..5f15176a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,83 @@ +name: Bug report +description: Something evaluates or behaves incorrectly +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + A minimal decision graph plus its input is by far the most useful thing you can give us. + Without one, most evaluation bugs cannot be reproduced. + - type: textarea + id: what-happened + attributes: + label: What happened + description: What you observed, and what you expected instead. + validations: + required: true + - type: textarea + id: graph + attributes: + label: Decision graph + description: The smallest JDM graph that reproduces this. + render: json + validations: + required: true + - type: textarea + id: input + attributes: + label: Input + description: The context passed to the evaluation. + render: json + validations: + required: true + - type: textarea + id: output + attributes: + label: Actual output + description: What came back, including any error. + render: json + validations: + required: false + - type: dropdown + id: binding + attributes: + label: How are you calling it? + options: + - Rust crate + - Go + - NodeJS + - Python + - C / FFI + - UniFFI + - Other + validations: + required: true + - type: input + id: version + attributes: + label: Version or commit + placeholder: "v1.0.0-beta.12, or a commit SHA" + validations: + required: true + - type: input + id: features + attributes: + label: Cargo features enabled + description: Numeric behaviour differs with `arbitrary_precision`, so this matters for anything involving numbers. + placeholder: "default, or arbitrary_precision" + validations: + required: false + - type: input + id: platform + attributes: + label: Platform + placeholder: "linux-x64-gnu, macOS arm64, ..." + validations: + required: false + - type: checkboxes + id: upstream + attributes: + label: Upstream + options: + - label: I checked whether this also reproduces on upstream `gorules/zen` + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..cac15faf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Bug in upstream ZEN, not specific to this fork + url: https://github.com/gorules/zen/issues + about: If it reproduces on gorules/zen, report it there too so both projects benefit. + - name: Security vulnerability + url: https://github.com/phenixrizen/zen/security/advisories/new + about: Report privately through a security advisory. Please do not open a public issue. + - name: JDM standard and documentation + url: https://docs.gorules.io + about: The JDM standard is GoRules'. Their documentation is the reference. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..dfdd3d08 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,32 @@ +name: Feature request +description: Suggest a capability or improvement +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: What problem are you trying to solve? + description: Describe the situation, not the solution. What can you not express today? + validations: + required: true + - type: textarea + id: workaround + attributes: + label: What are you doing instead right now? + description: If there is a workaround, showing it helps size the gap. + validations: + required: false + - type: textarea + id: proposal + attributes: + label: What would you like to see? + description: If you have a shape in mind, sketch it. A JDM snippet or API signature is ideal. + validations: + required: false + - type: checkboxes + id: scope + attributes: + label: Scope + options: + - label: This is a change to the JDM standard itself (GoRules owns the standard; this fork tries to stay compatible with it) + required: false diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..87583bf0 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policy + +## Reporting a vulnerability + +**Do not open a public issue for a security problem.** + +Report it privately through GitHub Security Advisories: +[Report a vulnerability](https://github.com/phenixrizen/zen/security/advisories/new). + +Please include what an attacker can achieve, a minimal reproduction (a JDM graph and input if the +issue is in evaluation), and the affected version or commit. + +## What to expect + +This fork is maintained by one person, so response times are best-effort rather than contractual: + +| stage | realistic timeframe | +| --- | --- | +| Acknowledgement | within a week | +| Initial assessment | within two weeks | +| Fix or mitigation plan | depends on severity and complexity | + +If you have not heard back in two weeks, please ping the advisory thread. + +## Scope + +In scope: the Rust crates in this repository — evaluation, expression handling, the database node +and its SQLite handler, and the bindings. + +Particularly interested in: anything that gets untrusted graph or expression input to escape its +evaluation boundary, reach the filesystem, or reach a database outside its configured root; and +any way to get a value into SQL statement text rather than a bound parameter. + +Out of scope: vulnerabilities in upstream `gorules/zen` that are not specific to this fork should +also be reported to [upstream](https://github.com/gorules/zen). Denial of service from a graph you +authored yourself is not a vulnerability — evaluation is not sandboxed against its own author. + +## Supported versions + +Only the latest `master` is supported. There are no backported security releases. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..3e66d584 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,28 @@ +## What this changes + + + +## Why + + + +## Testing + + + +- [ ] `cargo fmt --all -- --check` +- [ ] `cargo test --workspace --all-features --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --locked` +- [ ] `cargo test --workspace --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --locked` +- [ ] For a bug fix: the new test fails without the fix + +## Relationship to upstream + + + +- [ ] Fork-specific — builds on this fork's own additions +- [ ] Also affects upstream `gorules/zen` +- [ ] Merges cleanly with current upstream `master` + +## Notes for the reviewer + + diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml index 4ce1e83f..6602acf9 100644 --- a/.github/workflows/go.yaml +++ b/.github/workflows/go.yaml @@ -97,7 +97,9 @@ jobs: name: Release runs-on: ubuntu-latest environment: release - if: "github.ref_type == 'tag' || github.event_name == 'workflow_dispatch'" + # Fork guard: this job pushes built libraries into the gorules/zen-go + # repository. Forks build and test, but never publish. + if: "(github.ref_type == 'tag' || github.event_name == 'workflow_dispatch') && github.repository == 'gorules/zen'" needs: - build - copy-common diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 44d7a600..3ba51cf9 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -273,7 +273,7 @@ jobs: runs-on: ubuntu-latest environment: release # Publishes only on the release-please GitHub Release for the nodejs component. - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" defaults: run: working-directory: ${{ env.WORKING_DIRECTORY }} diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml index f448b413..1213310e 100644 --- a/.github/workflows/python.yaml +++ b/.github/workflows/python.yaml @@ -93,7 +93,7 @@ jobs: runs-on: ubuntu-latest environment: release # Publishes only on the release-please GitHub Release for the python component. - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" needs: [ wheels, sdist ] steps: - uses: actions/download-artifact@v4 diff --git a/.github/workflows/release-please.yaml b/.github/workflows/release-please.yaml index e6c7a631..248d4e9c 100644 --- a/.github/workflows/release-please.yaml +++ b/.github/workflows/release-please.yaml @@ -36,6 +36,9 @@ jobs: release-please: name: Release Please runs-on: ubuntu-latest + # Fork guard: release automation cuts tags that drive the publish jobs. + # Forks should not cut releases under the upstream version line. + if: "github.repository == 'gorules/zen'" steps: - uses: googleapis/release-please-action@v5 id: release diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index da7c2a6b..56678791 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -65,7 +65,7 @@ jobs: name: Cargo Release runs-on: ubuntu-latest environment: release - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" needs: - test - fmt diff --git a/.github/workflows/uniffi.yaml b/.github/workflows/uniffi.yaml index 705e1fff..745d3c26 100644 --- a/.github/workflows/uniffi.yaml +++ b/.github/workflows/uniffi.yaml @@ -230,7 +230,7 @@ jobs: runs-on: ubuntu-latest environment: release needs: [ build, test-jvm-dotnet ] - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" defaults: run: working-directory: ${{ env.UNIFFI_DIRECTORY }} @@ -306,7 +306,7 @@ jobs: needs: [ build, test-jvm-dotnet ] permissions: id-token: write # Required for OIDC - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" defaults: run: working-directory: ${{ env.UNIFFI_DIRECTORY }} @@ -541,7 +541,7 @@ jobs: runs-on: macos-latest environment: release needs: [ create-xcframework, test-swift ] - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" steps: - name: Checkout zen-ios @@ -649,7 +649,7 @@ jobs: add-to-path: true - name: Add Rust Android targets (all) - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" run: | rustup target add aarch64-linux-android rustup target add armv7-linux-androideabi @@ -662,7 +662,7 @@ jobs: rustup target add aarch64-linux-android - name: Build for Android (all targets) - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" env: ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} run: | @@ -827,7 +827,7 @@ jobs: runs-on: ubuntu-latest environment: release needs: [ package-android-aar ] - if: "github.ref_type == 'tag'" + if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" defaults: run: working-directory: ${{ env.UNIFFI_DIRECTORY }} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..44e909d5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,59 @@ +# Code of Conduct + +## Our pledge + +We as members, contributors, and leaders pledge to make participation in our community a +harassment-free experience for everyone, regardless of age, body size, visible or invisible +disability, ethnicity, sex characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, caste, color, religion, +or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, +and healthy community. + +## Our standards + +Examples of behavior that contributes to a positive environment: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes +* Focusing on what is best for the overall community + +Examples of unacceptable behavior: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their + explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement responsibilities + +Project maintainers are responsible for clarifying and enforcing these standards and will take +appropriate and fair corrective action in response to any behavior that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is +officially representing the community in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project +maintainers through a +[private security advisory](https://github.com/phenixrizen/zen/security/advisories/new) or by +contacting **@phenixrizen** directly on GitHub. + +All complaints will be reviewed and investigated promptly and fairly. Maintainers are obligated to +respect the privacy and security of the reporter of any incident. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available +at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. + +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..58fac086 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,97 @@ +# Contributing + +Contributions are welcome. This is a maintained fork of +[`gorules/zen`](https://github.com/gorules/zen); upstream states it cannot accept code +contributions, so this fork exists to take them. + +Maintained by Phenix Rizen (Nathan Rockhold). + +## Scope: what belongs here + +| belongs in this fork | send upstream instead | +| --- | --- | +| Fixes upstream has not responded to | Anything you would rather see in the canonical project first | +| The fork's own additions (`databaseNode`, `zen-database-sqlite`, `$params`) | JDM standard/spec changes — GoRules owns the standard | +| Correctness fixes with a regression test | — | + +A bug that reproduces on upstream is worth reporting +[there](https://github.com/gorules/zen/issues) too. Both projects benefit, and this fork tracks +upstream `master`. + +## Development setup + +Requires a stable Rust toolchain. Nothing else — the SQLite handler is pure Rust +([Turso](https://github.com/tursodatabase/turso)), so there is no C compiler or vendored +amalgamation in the build. + +```bash +git clone https://github.com/phenixrizen/zen.git +cd zen +cargo build --workspace +``` + +## The test gate + +Run all four commands before opening a PR. CI runs the same ones. + +```bash +# 1. Formatting. +cargo fmt --all -- --check + +# 2. Full workspace, all features. +cargo test --workspace --all-features \ + --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --locked + +# 3. Again with default features. CI runs both; feature unification means +# they do not exercise the same code. +cargo test --workspace \ + --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --locked + +# 4. WASM still builds. +cargo check -p zen-engine --target wasm32-wasip1-threads --all-features +``` + +### Things that will trip you up + +- **`arbitrary_precision` changes numeric output.** With it off, numbers serialize through `f64`. + Some upstream fixtures (`customer-lifetime-value.json`) encode 28-digit decimal expectations and + can only pass with the feature on, which is why step 2 and step 3 differ. If you touch numeric + serialization, run both. +- **Snapshot tests.** `cargo insta` review if you change engine output. A PR should not carry + unexplained `.snap` changes. +- **Tests that need reference data skip by default.** `zen-database-sqlite`'s differential suites + read `ZEN_REFDATA_DIR` and `ZEN_CORPUS_MANIFEST`. Unset means "no bundle available" and they + skip. Never commit a `.db` fixture — build read-only fixtures at test time from a committed + `.sql`. + +## Commit messages + +[Conventional Commits](https://www.conventionalcommits.org/). Release automation parses them, so +the prefix determines the version bump. + +``` +fix(expression): honour TZ when resolving the local timezone +feat(engine): add database node and handler extension point +test(database-sqlite): require an explicit refdata bundle path +``` + +Explain *why* in the body, not just what. A reviewer can read the diff; they cannot read your +reasoning. + +## Pull requests + +- Branch off `master`. +- One logical change per PR. +- A bug fix needs a regression test, and you should confirm the test **fails without the fix**. + State that you did. +- Do not commit absolute paths, machine-specific defaults, or generated artifacts. + +## Tracking upstream + +```bash +git remote add upstream https://github.com/gorules/zen.git +git fetch upstream +git merge upstream/master +``` + +Keep fork-specific changes additive where practical, so upstream merges stay cheap. diff --git a/README.md b/README.md index 9c0e5d2a..91a0c57c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,31 @@ +> [!NOTE] +> ## This is a maintained fork +> +> **`phenixrizen/zen` is a community-maintained fork of [`gorules/zen`](https://github.com/gorules/zen), maintained by Phenix Rizen (Nathan Rockhold).** +> +> It exists because upstream states it cannot accept code contributions, and submitted fixes go +> unreviewed in practice — including a straightforward timezone defect fix with a regression test, +> which has sat without a response. +> +> This fork tracks upstream `master` and **does accept contributions**. See +> [CONTRIBUTING.md](CONTRIBUTING.md). +> +> ### What this fork adds +> +> | addition | what it does | +> | --- | --- | +> | `databaseNode` + `DatabaseHandler` | A first-class node for looking reference data up from a decision graph, with a host extension point. Values are always bound, never interpolated. | +> | `zen-database-sqlite` | A **pure-Rust** SQLite handler built on [Turso](https://github.com/tursodatabase/turso) — no C, no vendored amalgamation, no `cc` in your build. | +> | Decision-level `$params` | Static parameters supplied per decision and reachable from switch, expression, and function nodes. | +> | `TZ` is honoured | `local` timezone resolution respects the `TZ` environment variable instead of only `/etc/localtime`. | +> | Exact fractional numbers | Fixes silent truncation of every non-integer value when `arbitrary_precision` is off. | +> +> The badges below point at the upstream packages. **This fork does not publish to crates.io, npm, +> PyPI, or NuGet** — build it from source or depend on it by git reference. +> +> Not affiliated with or endorsed by GoRules. JDM remains GoRules' standard and the documentation +> links below point at their docs. MIT licensed, same as upstream. + # ZEN Engine **Business logic humans can read and machines can run.** One copy of your rules: the owner reads it, every system runs it. @@ -150,7 +178,24 @@ Mobile: **Swift (iOS XCFramework)** and **Android (AAR)** packages are published ## Contribution -The JDM standard is growing and we need to keep tight control over its development and roadmap, as a number of companies use GoRules ZEN Engine and GoRules BRMS. For this reason we can't accept code contributions at this moment, apart from help with documentation and additional tests. +**Contributions are welcome here.** This fork exists partly because upstream cannot take them. + +Read [CONTRIBUTING.md](CONTRIBUTING.md) for the development setup and the full test gate. In short: + +```bash +cargo fmt --all -- --check +cargo test --workspace --all-features \ + --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --locked +cargo test --workspace \ + --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --locked +``` + +Security issues should not go in a public issue — see [SECURITY.md](.github/SECURITY.md). + +A bug that also reproduces on upstream `gorules/zen` is worth reporting +[there](https://github.com/gorules/zen/issues) as well, so both projects benefit. + +The JDM standard itself is GoRules'. This fork aims to stay compatible with it rather than diverge. ## License From 0bfb2335d547aa5594f847e6ffc2c28b5320f91a Mon Sep 17 00:00:00 2001 From: Phenix Rizen Date: Thu, 27 Aug 2026 07:04:08 -0500 Subject: [PATCH 2/4] docs: add fork copyright and drop internal terminology MIT requires retaining the original copyright notice, so the upstream line stays and a second line is added for this fork's modifications. Also renames the reference-data environment variable to ZEN_CATALOG_DIR. Co-Authored-By: Claude Opus 5 (1M context) --- CONTRIBUTING.md | 4 ++-- LICENSE | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58fac086..240c395b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,7 +60,7 @@ cargo check -p zen-engine --target wasm32-wasip1-threads --all-features - **Snapshot tests.** `cargo insta` review if you change engine output. A PR should not carry unexplained `.snap` changes. - **Tests that need reference data skip by default.** `zen-database-sqlite`'s differential suites - read `ZEN_REFDATA_DIR` and `ZEN_CORPUS_MANIFEST`. Unset means "no bundle available" and they + read `ZEN_CATALOG_DIR` and `ZEN_CORPUS_MANIFEST`. Unset means "no bundle available" and they skip. Never commit a `.db` fixture — build read-only fixtures at test time from a committed `.sql`. @@ -72,7 +72,7 @@ the prefix determines the version bump. ``` fix(expression): honour TZ when resolving the local timezone feat(engine): add database node and handler extension point -test(database-sqlite): require an explicit refdata bundle path +test(database-sqlite): require an explicit catalog bundle path ``` Explain *why* in the body, not just what. A reviewer can read the diff; they cannot read your diff --git a/LICENSE b/LICENSE index ca6d24c6..8d4519a0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright 2023 GoRules.io +Copyright 2026 Phenix Rizen (Nathan Rockhold) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, From 01d81d07b8fa7687a73bb562ba38ff9e49cb787c Mon Sep 17 00:00:00 2001 From: Phenix Rizen Date: Thu, 27 Aug 2026 07:13:53 -0500 Subject: [PATCH 3/4] feat: publish under phenixrizen package names If this fork is maintained, it has to be installable. It could not publish before: every package name belonged to upstream, so the release jobs were either guarded off or would have failed on a registry we do not own. Renames the published artifacts and removes the guards: crates.io zen-engine -> phenixrizen-zen-engine (and -expression, -types, -tmpl, -macros) npm @gorules/... -> @phenixrizen/zen-engine PyPI zen-engine -> phenixrizen-zen-engine NuGet GoRules.ZenEngine -> PhenixRizen.ZenEngine Each Rust crate gains an explicit [lib] name matching its old package name, so `use zen_engine::...` still resolves and no source file changes. Dependents keep their familiar key and carry `package = "..."`. Repoints the Go release job at phenixrizen/zen-go, and updates crate metadata (authors, repository, homepage) to this fork. README: badges, install snippets and the bindings list now name the fork's packages. Drops upstream's platform section and marketing images, which advertise a product this repository is not part of. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/go.yaml | 8 +- .github/workflows/node.yaml | 2 +- .github/workflows/python.yaml | 2 +- .github/workflows/release-please.yaml | 3 - .github/workflows/rust.yaml | 2 +- .github/workflows/uniffi.yaml | 14 +- Cargo.lock | 256 +++++++++--------- README.md | 72 ++--- benchmark/rust/Cargo.toml | 4 +- bindings/c/Cargo.toml | 6 +- bindings/nodejs/Cargo.toml | 6 +- bindings/nodejs/package.json | 10 +- bindings/python/Cargo.toml | 6 +- bindings/python/pyproject.toml | 8 +- bindings/uniffi/Cargo.toml | 4 +- ...ne.csproj => PhenixRizen.ZenEngine.csproj} | 8 +- core/engine/Cargo.toml | 17 +- core/expression/Cargo.toml | 17 +- core/expression_repl/Cargo.toml | 2 +- core/macros/Cargo.toml | 9 +- core/template/Cargo.toml | 15 +- core/types/Cargo.toml | 11 +- 22 files changed, 237 insertions(+), 245 deletions(-) rename bindings/uniffi/nuget/{GoRules.ZenEngine.csproj => PhenixRizen.ZenEngine.csproj} (90%) diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml index 6602acf9..b060a7f7 100644 --- a/.github/workflows/go.yaml +++ b/.github/workflows/go.yaml @@ -97,9 +97,7 @@ jobs: name: Release runs-on: ubuntu-latest environment: release - # Fork guard: this job pushes built libraries into the gorules/zen-go - # repository. Forks build and test, but never publish. - if: "(github.ref_type == 'tag' || github.event_name == 'workflow_dispatch') && github.repository == 'gorules/zen'" + if: "(github.ref_type == 'tag' || github.event_name == 'workflow_dispatch')" needs: - build - copy-common @@ -107,7 +105,7 @@ jobs: - name: Checkout zen-go uses: actions/checkout@v3 with: - repository: gorules/zen-go + repository: phenixrizen/zen-go persist-credentials: false token: ${{ secrets.PAT }} - name: Download all artifacts @@ -148,7 +146,7 @@ jobs: - name: Push changes uses: ad-m/github-push-action@master with: - repository: gorules/zen-go + repository: phenixrizen/zen-go github_token: ${{ secrets.PAT }} branch: ${{ env.BRANCH_NAME }} - name: Create PR diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 3ba51cf9..44d7a600 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -273,7 +273,7 @@ jobs: runs-on: ubuntu-latest environment: release # Publishes only on the release-please GitHub Release for the nodejs component. - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" defaults: run: working-directory: ${{ env.WORKING_DIRECTORY }} diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml index 1213310e..f448b413 100644 --- a/.github/workflows/python.yaml +++ b/.github/workflows/python.yaml @@ -93,7 +93,7 @@ jobs: runs-on: ubuntu-latest environment: release # Publishes only on the release-please GitHub Release for the python component. - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" needs: [ wheels, sdist ] steps: - uses: actions/download-artifact@v4 diff --git a/.github/workflows/release-please.yaml b/.github/workflows/release-please.yaml index 248d4e9c..e6c7a631 100644 --- a/.github/workflows/release-please.yaml +++ b/.github/workflows/release-please.yaml @@ -36,9 +36,6 @@ jobs: release-please: name: Release Please runs-on: ubuntu-latest - # Fork guard: release automation cuts tags that drive the publish jobs. - # Forks should not cut releases under the upstream version line. - if: "github.repository == 'gorules/zen'" steps: - uses: googleapis/release-please-action@v5 id: release diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index 56678791..da7c2a6b 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -65,7 +65,7 @@ jobs: name: Cargo Release runs-on: ubuntu-latest environment: release - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" needs: - test - fmt diff --git a/.github/workflows/uniffi.yaml b/.github/workflows/uniffi.yaml index 745d3c26..611fb84e 100644 --- a/.github/workflows/uniffi.yaml +++ b/.github/workflows/uniffi.yaml @@ -230,7 +230,7 @@ jobs: runs-on: ubuntu-latest environment: release needs: [ build, test-jvm-dotnet ] - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" defaults: run: working-directory: ${{ env.UNIFFI_DIRECTORY }} @@ -306,7 +306,7 @@ jobs: needs: [ build, test-jvm-dotnet ] permissions: id-token: write # Required for OIDC - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" defaults: run: working-directory: ${{ env.UNIFFI_DIRECTORY }} @@ -362,7 +362,7 @@ jobs: echo "Detected version: $VERSION" - name: Pack NuGet package - run: dotnet pack nuget/GoRules.ZenEngine.csproj -c Release -p:Version=$VERSION + run: dotnet pack nuget/PhenixRizen.ZenEngine.csproj -c Release -p:Version=$VERSION - name: Upload NuGet package artifact uses: actions/upload-artifact@v4 @@ -541,7 +541,7 @@ jobs: runs-on: macos-latest environment: release needs: [ create-xcframework, test-swift ] - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" steps: - name: Checkout zen-ios @@ -649,7 +649,7 @@ jobs: add-to-path: true - name: Add Rust Android targets (all) - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" run: | rustup target add aarch64-linux-android rustup target add armv7-linux-androideabi @@ -662,7 +662,7 @@ jobs: rustup target add aarch64-linux-android - name: Build for Android (all targets) - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" env: ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} run: | @@ -827,7 +827,7 @@ jobs: runs-on: ubuntu-latest environment: release needs: [ package-android-aar ] - if: "github.ref_type == 'tag' && github.repository == 'gorules/zen'" + if: "github.ref_type == 'tag'" defaults: run: working-directory: ${{ env.UNIFFI_DIRECTORY }} diff --git a/Cargo.lock b/Cargo.lock index 76c410c1..b1f701fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1205,9 +1205,9 @@ name = "expression_repl" version = "2.0.1" dependencies = [ "colored", + "phenixrizen-zen-expression", "rustyline", "serde_json", - "zen-expression", ] [[package]] @@ -2765,6 +2765,120 @@ dependencies = [ "serde", ] +[[package]] +name = "phenixrizen-zen-engine" +version = "2.0.1" +dependencies = [ + "ahash 0.8.12", + "anyhow", + "arc-swap", + "async-trait", + "base64", + "criterion", + "downcast-rs", + "fixedbitset", + "flate2", + "http", + "insta", + "json_dotpath", + "jsonschema", + "mimalloc", + "nohash-hasher", + "once_cell", + "petgraph", + "phenixrizen-zen-expression", + "phenixrizen-zen-tmpl", + "phenixrizen-zen-types", + "reqsign", + "reqwest 0.12.28", + "rquickjs", + "rust_decimal", + "self_cell", + "serde", + "serde_json", + "serde_path_to_error", + "sha2 0.10.9", + "strum 0.27.2", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ts_fast_strip", + "thiserror 1.0.69", + "tokio", + "toml", + "typed-arena", + "zip", +] + +[[package]] +name = "phenixrizen-zen-expression" +version = "2.0.1" +dependencies = [ + "ahash 0.8.12", + "anyhow", + "bumpalo", + "chrono", + "chrono-tz", + "criterion", + "csv", + "fastrand", + "humantime", + "iana-time-zone", + "nohash-hasher", + "once_cell", + "phenixrizen-zen-macros", + "phenixrizen-zen-types", + "recursive", + "regex", + "regex-lite", + "rust_decimal", + "rust_decimal_macros", + "serde", + "serde_json", + "serde_json5", + "smallvec", + "strsim", + "strum 0.27.2", + "strum_macros 0.27.2", + "thiserror 1.0.69", + "toml", +] + +[[package]] +name = "phenixrizen-zen-macros" +version = "2.0.1" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + +[[package]] +name = "phenixrizen-zen-tmpl" +version = "2.0.1" +dependencies = [ + "phenixrizen-zen-expression", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "phenixrizen-zen-types" +version = "2.0.1" +dependencies = [ + "ahash 0.8.12", + "hipstr", + "nohash-hasher", + "rust_decimal", + "rust_decimal_macros", + "serde", + "serde_json", + "smallvec", + "thiserror 1.0.69", +] + [[package]] name = "phf" version = "0.11.3" @@ -5994,90 +6108,11 @@ dependencies = [ name = "zen-benchmark" version = "0.0.16" dependencies = [ + "phenixrizen-zen-engine", + "phenixrizen-zen-expression", "serde", "serde_json", "tokio", - "zen-engine", - "zen-expression", -] - -[[package]] -name = "zen-engine" -version = "2.0.1" -dependencies = [ - "ahash 0.8.12", - "anyhow", - "arc-swap", - "async-trait", - "base64", - "criterion", - "downcast-rs", - "fixedbitset", - "flate2", - "http", - "insta", - "json_dotpath", - "jsonschema", - "mimalloc", - "nohash-hasher", - "once_cell", - "petgraph", - "reqsign", - "reqwest 0.12.28", - "rquickjs", - "rust_decimal", - "self_cell", - "serde", - "serde_json", - "serde_path_to_error", - "sha2 0.10.9", - "strum 0.27.2", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ts_fast_strip", - "thiserror 1.0.69", - "tokio", - "toml", - "typed-arena", - "zen-expression", - "zen-tmpl", - "zen-types", - "zip", -] - -[[package]] -name = "zen-expression" -version = "2.0.1" -dependencies = [ - "ahash 0.8.12", - "anyhow", - "bumpalo", - "chrono", - "chrono-tz", - "criterion", - "csv", - "fastrand", - "humantime", - "iana-time-zone", - "nohash-hasher", - "once_cell", - "recursive", - "regex", - "regex-lite", - "rust_decimal", - "rust_decimal_macros", - "serde", - "serde_json", - "serde_json5", - "smallvec", - "strsim", - "strum 0.27.2", - "strum_macros 0.27.2", - "thiserror 1.0.69", - "toml", - "zen-macros", - "zen-types", ] [[package]] @@ -6087,24 +6122,14 @@ dependencies = [ "anyhow", "cbindgen", "libc", + "phenixrizen-zen-engine", + "phenixrizen-zen-expression", + "phenixrizen-zen-tmpl", "serde", "serde_json", "strum 0.27.2", "tokio", "tokio-util", - "zen-engine", - "zen-expression", - "zen-tmpl", -] - -[[package]] -name = "zen-macros" -version = "2.0.1" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", ] [[package]] @@ -6118,12 +6143,12 @@ dependencies = [ "napi-build", "napi-derive", "napi-sys", + "phenixrizen-zen-engine", + "phenixrizen-zen-expression", + "phenixrizen-zen-tmpl", "serde", "serde_json", "tokio-util", - "zen-engine", - "zen-expression", - "zen-tmpl", ] [[package]] @@ -6133,6 +6158,9 @@ dependencies = [ "anyhow", "either", "json_dotpath", + "phenixrizen-zen-engine", + "phenixrizen-zen-expression", + "phenixrizen-zen-tmpl", "pyo3", "pyo3-async-runtimes", "pythonize", @@ -6140,34 +6168,6 @@ dependencies = [ "serde", "serde_json", "tokio-util", - "zen-engine", - "zen-expression", - "zen-tmpl", -] - -[[package]] -name = "zen-tmpl" -version = "2.0.1" -dependencies = [ - "serde", - "serde_json", - "thiserror 1.0.69", - "zen-expression", -] - -[[package]] -name = "zen-types" -version = "2.0.1" -dependencies = [ - "ahash 0.8.12", - "hipstr", - "nohash-hasher", - "rust_decimal", - "rust_decimal_macros", - "serde", - "serde_json", - "smallvec", - "thiserror 1.0.69", ] [[package]] @@ -6175,12 +6175,12 @@ name = "zen-uniffi" version = "2.0.2" dependencies = [ "async-trait", + "phenixrizen-zen-engine", + "phenixrizen-zen-expression", "serde", "serde_json", "tokio", "uniffi", - "zen-engine", - "zen-expression", ] [[package]] diff --git a/README.md b/README.md index 91a0c57c..dc84d43e 100644 --- a/README.md +++ b/README.md @@ -20,27 +20,34 @@ > | `TZ` is honoured | `local` timezone resolution respects the `TZ` environment variable instead of only `/etc/localtime`. | > | Exact fractional numbers | Fixes silent truncation of every non-integer value when `arbitrary_precision` is off. | > -> The badges below point at the upstream packages. **This fork does not publish to crates.io, npm, -> PyPI, or NuGet** — build it from source or depend on it by git reference. +> ### Packages > -> Not affiliated with or endorsed by GoRules. JDM remains GoRules' standard and the documentation -> links below point at their docs. MIT licensed, same as upstream. +> This fork publishes under its own names, so it never collides with upstream: +> +> | | package | +> | --- | --- | +> | Rust | `phenixrizen-zen-engine` (also `-expression`, `-types`, `-tmpl`, `-macros`) | +> | Node.js | `@phenixrizen/zen-engine` | +> | Python | `phenixrizen-zen-engine` | +> | .NET | `PhenixRizen.ZenEngine` | +> +> The Rust crates keep their original library names, so `use zen_engine::…` is unchanged — only +> the dependency line moves. +> +> Not affiliated with or endorsed by GoRules. MIT licensed, same as upstream, with the original +> copyright retained in [LICENSE](LICENSE). # ZEN Engine **Business logic humans can read and machines can run.** One copy of your rules: the owner reads it, every system runs it. [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![crates.io](https://img.shields.io/crates/v/zen-engine.svg)](https://crates.io/crates/zen-engine) -[![npm](https://img.shields.io/npm/v/@gorules/zen-engine.svg)](https://www.npmjs.com/package/@gorules/zen-engine) -[![PyPI](https://img.shields.io/pypi/v/zen-engine.svg)](https://pypi.org/project/zen-engine/) - -GoRules ZEN Engine +[![crates.io](https://img.shields.io/crates/v/phenixrizen-zen-engine.svg)](https://crates.io/crates/phenixrizen-zen-engine) +[![npm](https://img.shields.io/npm/v/@phenixrizen/zen-engine.svg)](https://www.npmjs.com/package/@phenixrizen/zen-engine) +[![PyPI](https://img.shields.io/pypi/v/phenixrizen-zen-engine.svg)](https://pypi.org/project/phenixrizen-zen-engine/) ZEN Engine is a cross-platform, open-source Business Rules Engine (BRE) written in **Rust**, with native bindings for **Node.js**, **Python**, **Go**, **Java**, **Kotlin** and **.NET**, plus iOS and Android packages. Decisions evaluate in microseconds, run identically on every platform, and are stored as portable JSON. Loading the JSON is up to you: file system, database or service call. -Try it in the free [Online Editor](https://editor.gorules.io) with a built-in simulator, or embed the open-source React [JDM Editor](https://github.com/gorules/jdm-editor) in your own product. - ## Rules that read like sentences Conditions are written the way the business says them, in the ZEN Expression Language. The developer view is one toggle away, and the two can never drift apart: there is only one source of truth, and this engine runs it. @@ -53,7 +60,7 @@ Model a decision on a visual canvas of decision tables, switches, expressions, f Graphs and documents -To go deeper on the decision model and each node type, see the [JDM documentation](https://gorules.io/docs/rules-engine/json-decision-model) and the [ZEN Expression Language](https://gorules.io/docs/rules-engine/expression-language/) reference. +JDM is the JSON Decision Model: the document format this engine loads and evaluates. A JDM document is either a **graph** (decision tables, switches, expressions, functions and reusable sub-decisions on a canvas) or a **policy** (prose, typed data models and tables). Both compile to the same engine and return the same answers. ## What's new in 2.0 @@ -75,7 +82,7 @@ Version 2.0 is the first stable release of the new engine line: ```toml [dependencies] -zen-engine = "2" +phenixrizen-zen-engine = "2" ``` ```rust @@ -97,11 +104,11 @@ async fn evaluate() { ### Node.js ```bash -npm i @gorules/zen-engine +npm i @phenixrizen/zen-engine ``` ```typescript -import { ZenEngine } from '@gorules/zen-engine'; +import { ZenEngine } from '@phenixrizen/zen-engine'; import fs from 'fs/promises'; const content = await fs.readFile('./jdm_graph.json'); @@ -114,7 +121,7 @@ const result = await decision.evaluate({ input: 15 }); ### Python ```bash -pip install zen-engine +pip install phenixrizen-zen-engine ``` ```python @@ -131,34 +138,13 @@ result = decision.evaluate({"input": 15}) Full guides, including loaders for multi-decision graphs and batch evaluation: -* **Node.js** - [GitHub](https://github.com/gorules/zen/blob/master/bindings/nodejs/README.md) | [Documentation](https://gorules.io/docs/developers/bre/engines/nodejs) | [npmjs](https://www.npmjs.com/package/@gorules/zen-engine) -* **Python** - [GitHub](https://github.com/gorules/zen/blob/master/bindings/python/README.md) | [Documentation](https://gorules.io/docs/developers/bre/engines/python) | [pypi](https://pypi.org/project/zen-engine/) -* **Go** - [GitHub](https://github.com/gorules/zen-go) | [Documentation](https://gorules.io/docs/developers/bre/engines/go) -* **Java / Kotlin** - [GitHub](https://github.com/gorules/zen/blob/master/bindings/uniffi) | [Maven Central](https://mvnrepository.com/artifact/io.gorules/zen-engine) -* **.NET** - [GitHub](https://github.com/gorules/zen/blob/master/bindings/uniffi) | [NuGet](https://www.nuget.org/packages/GoRules.ZenEngine) -* **Rust (Core)** - [GitHub](https://github.com/gorules/zen) | [Documentation](https://gorules.io/docs/developers/bre/engines/rust) | [crates.io](https://crates.io/crates/zen-engine) - -## The GoRules platform - -The engine is open at the core; [GoRules](https://gorules.io) is the platform around it. Managed cloud, self-hosted, or embedded with no network hop. SOC 2 Type II. - -### AI that builds rules, and stays reviewable - -An AI copilot and MCP server that edits rules, runs tests and explains decisions. It never deploys. Releases stay with your reviewers. - -GoRules AI - -### Promote like a release, run like a binary - -A release moves from testing to staging to production untouched. Approvals, instant rollback, and a paper trail for every change. - -Governance - -### Prove it before it ships - -Scenario suites run on every change, coverage is measured against decision paths, and every answer comes with a replayable trace. +* **Node.js** — [source](bindings/nodejs/README.md) | [npm](https://www.npmjs.com/package/@phenixrizen/zen-engine) +* **Python** — [source](bindings/python/README.md) | [PyPI](https://pypi.org/project/phenixrizen-zen-engine/) +* **Go** — [phenixrizen/zen-go](https://github.com/phenixrizen/zen-go) +* **Java / Kotlin** — [source](bindings/uniffi) +* **.NET** — [source](bindings/uniffi) | [NuGet](https://www.nuget.org/packages/PhenixRizen.ZenEngine) +* **Rust (core)** — [source](core/engine) | [crates.io](https://crates.io/crates/phenixrizen-zen-engine) -Testing ## Support matrix diff --git a/benchmark/rust/Cargo.toml b/benchmark/rust/Cargo.toml index c92fc6b1..dd09db79 100644 --- a/benchmark/rust/Cargo.toml +++ b/benchmark/rust/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" publish = false [dependencies] -zen-engine = { path = "../../core/engine" } -zen-expression = { path = "../../core/expression" } +zen-engine = { package = "phenixrizen-zen-engine", path = "../../core/engine" } +zen-expression = { package = "phenixrizen-zen-expression", path = "../../core/expression" } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml index 994f25e0..a46a03c7 100644 --- a/bindings/c/Cargo.toml +++ b/bindings/c/Cargo.toml @@ -13,9 +13,9 @@ serde_json = { workspace = true } strum = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["rt"] } tokio-util = { workspace = true, features = ["rt"] } -zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } -zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } -zen-tmpl = { path = "../../core/template" } +zen-engine = { package = "phenixrizen-zen-engine", path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { package = "phenixrizen-zen-expression", path = "../../core/expression", features = ["arbitrary_precision"] } +zen-tmpl = { package = "phenixrizen-zen-tmpl", path = "../../core/template" } [lib] crate-type = ["staticlib"] diff --git a/bindings/nodejs/Cargo.toml b/bindings/nodejs/Cargo.toml index fee8c62d..439b340a 100644 --- a/bindings/nodejs/Cargo.toml +++ b/bindings/nodejs/Cargo.toml @@ -15,9 +15,9 @@ napi-sys = "3" napi-derive = "3" tokio-util = { workspace = true, features = ["rt"] } serde_json = { workspace = true } -zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } -zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } -zen-tmpl = { path = "../../core/template" } +zen-engine = { package = "phenixrizen-zen-engine", path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { package = "phenixrizen-zen-expression", path = "../../core/expression", features = ["arbitrary_precision"] } +zen-tmpl = { package = "phenixrizen-zen-tmpl", path = "../../core/template" } serde = { workspace = true, features = ["derive"] } json_dotpath = { workspace = true } diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json index eec287f9..2f27ba6c 100644 --- a/bindings/nodejs/package.json +++ b/bindings/nodejs/package.json @@ -1,5 +1,5 @@ { - "name": "@gorules/zen-engine", + "name": "@phenixrizen/zen-engine", "description": "Open-source Business Rules Engine for Node.js, powered by Rust", "version": "2.0.2", "main": "index.js", @@ -56,8 +56,8 @@ "napi-rs", "node-rs" ], - "author": "GoRules (https://gorules.io)", - "homepage": "https://gorules.io", + "author": "Phenix Rizen ", + "homepage": "https://github.com/phenixrizen/zen", "engines": { "node": ">= 14" }, @@ -79,7 +79,7 @@ "typescript": "^5.9.3" }, "bugs": { - "url": "https://github.com/gorules/zen/issues" + "url": "https://github.com/phenixrizen/zen/issues" }, "publishConfig": { "access": "public", @@ -87,7 +87,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/gorules/zen.git" + "url": "git+https://github.com/phenixrizen/zen.git" }, "scripts": { "build": "napi build --dts temp.d.ts --platform --release", diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index a289e098..54d835dc 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -20,6 +20,6 @@ serde = { workspace = true } serde_json = { workspace = true } rust_decimal = { workspace = true, features = ["maths-nopanic"] } tokio-util = { version = "0.7", features = ["rt"] } -zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } -zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } -zen-tmpl = { path = "../../core/template" } +zen-engine = { package = "phenixrizen-zen-engine", path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { package = "phenixrizen-zen-expression", path = "../../core/expression", features = ["arbitrary_precision"] } +zen-tmpl = { package = "phenixrizen-zen-tmpl", path = "../../core/template" } diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml index bce65bd5..1d71fdf8 100644 --- a/bindings/python/pyproject.toml +++ b/bindings/python/pyproject.toml @@ -3,7 +3,7 @@ requires = ["maturin>=1,<2"] build-backend = "maturin" [project] -name = "zen-engine" +name = "phenixrizen-zen-engine" requires-python = ">=3.7" classifiers = [ "Programming Language :: Rust", @@ -11,9 +11,9 @@ classifiers = [ "Programming Language :: Python :: Implementation :: PyPy", ] -description = "Open-Source Business Rules Engine" +description = "Open-Source Business Rules Engine (maintained fork of gorules/zen)" readme = "README.md" -authors = [{ name = "GoRules Team", email = "hi@gorules.io" }] +authors = [{ name = "Phenix Rizen", email = "phenixrizen@gmail.com" }] license = { file = "LICENSE" } keywords = ["gorules", @@ -37,7 +37,7 @@ dev = ["black", "bumpver", "isort", "pip-tools", "pytest", "asyncio"] [project.urls] Homepage = "https://gorules.io" Documentation = "https://docs.gorules.io/developers/sdks/python" -Repository = "https://github.com/gorules/zen" +Repository = "https://github.com/phenixrizen/zen" [project.scripts] zenengine = "reader.__main__:main" diff --git a/bindings/uniffi/Cargo.toml b/bindings/uniffi/Cargo.toml index 141bc651..f689b16c 100644 --- a/bindings/uniffi/Cargo.toml +++ b/bindings/uniffi/Cargo.toml @@ -15,8 +15,8 @@ path = "uniffi-bindgen.rs" [dependencies] uniffi = { version = "0.31", features = ["tokio", "cli"] } serde_json = { workspace = true } -zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } -zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } +zen-engine = { package = "phenixrizen-zen-engine", path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { package = "phenixrizen-zen-expression", path = "../../core/expression", features = ["arbitrary_precision"] } serde = { workspace = true, features = ["derive"] } async-trait = "0.1" tokio = "1.46" diff --git a/bindings/uniffi/nuget/GoRules.ZenEngine.csproj b/bindings/uniffi/nuget/PhenixRizen.ZenEngine.csproj similarity index 90% rename from bindings/uniffi/nuget/GoRules.ZenEngine.csproj rename to bindings/uniffi/nuget/PhenixRizen.ZenEngine.csproj index af1e2e99..0be5b430 100644 --- a/bindings/uniffi/nuget/GoRules.ZenEngine.csproj +++ b/bindings/uniffi/nuget/PhenixRizen.ZenEngine.csproj @@ -2,14 +2,14 @@ net8.0 - GoRules.ZenEngine - GoRules + PhenixRizen.ZenEngine + Phenix Rizen GoRules ZEN Engine - Business Rules Engine for .NET. Execute JSON Decision Models (JDM) with native performance via Rust FFI bindings. MIT - https://gorules.io + https://github.com/phenixrizen/zen icon.png - https://github.com/gorules/zen + https://github.com/phenixrizen/zen rules-engine;business-rules;decision-engine;jdm;gorules enable enable diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index d376012b..5b37829b 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -1,14 +1,15 @@ [package] -authors = ["GoRules Team "] -description = "Business rules engine" -name = "zen-engine" +authors = ["Phenix Rizen ", "GoRules Team "] +description = "Business rules engine (maintained fork of gorules/zen)" +name = "phenixrizen-zen-engine" license = "MIT" version = "2.0.1" edition = "2021" -repository = "https://github.com/gorules/zen.git" -homepage = "https://gorules.io" +repository = "https://github.com/phenixrizen/zen.git" +homepage = "https://github.com/phenixrizen/zen" [lib] +name = "zen_engine" doctest = false [dependencies] @@ -27,9 +28,9 @@ rust_decimal = { workspace = true, features = ["maths-nopanic"] } fixedbitset = "0.5" tokio = { workspace = true, features = ["sync", "time"] } rquickjs = { version = "0.10", features = ["macro", "loader", "rust-alloc", "futures", "either", "properties"] } -zen-types = { path = "../types", version = "2.0.1" } -zen-expression = { path = "../expression", version = "2.0.1" } -zen-tmpl = { path = "../template", version = "2.0.1" } +zen-types = { package = "phenixrizen-zen-types", path = "../types", version = "2.0.1" } +zen-expression = { package = "phenixrizen-zen-expression", path = "../expression", version = "2.0.1" } +zen-tmpl = { package = "phenixrizen-zen-tmpl", path = "../template", version = "2.0.1" } nohash-hasher = { workspace = true } downcast-rs = { version = "2.0", features = ["std", "sync"] } arc-swap = "1" diff --git a/core/expression/Cargo.toml b/core/expression/Cargo.toml index f43e1e29..65e23206 100644 --- a/core/expression/Cargo.toml +++ b/core/expression/Cargo.toml @@ -1,12 +1,15 @@ [package] -authors = ["GoRules Team "] -description = "Zen Expression Language" -name = "zen-expression" +authors = ["Phenix Rizen ", "GoRules Team "] +description = "Zen Expression Language (maintained fork of gorules/zen)" +name = "phenixrizen-zen-expression" license = "MIT" version = "2.0.1" edition = "2021" -repository = "https://github.com/gorules/zen.git" -homepage = "https://gorules.io" +repository = "https://github.com/phenixrizen/zen.git" +homepage = "https://github.com/phenixrizen/zen" + +[lib] +name = "zen_expression" [dependencies] smallvec = { version = "1", features = ["union"] } @@ -31,8 +34,8 @@ nohash-hasher = { workspace = true } strsim = "0.11" iana-time-zone = "0.1" -zen-macros = { path = "../macros", version = "2.0.1" } -zen-types = { path = "../types", version = "2.0.1" } +zen-macros = { package = "phenixrizen-zen-macros", path = "../macros", version = "2.0.1" } +zen-types = { package = "phenixrizen-zen-types", path = "../types", version = "2.0.1" } [dev-dependencies] criterion = { workspace = true } diff --git a/core/expression_repl/Cargo.toml b/core/expression_repl/Cargo.toml index 0943beb9..02a8825a 100644 --- a/core/expression_repl/Cargo.toml +++ b/core/expression_repl/Cargo.toml @@ -10,5 +10,5 @@ publish = false colored = "3" rustyline = "15" serde_json = { workspace = true } -zen-expression = { path = "../expression", version = "2.0.1" } +zen-expression = { package = "phenixrizen-zen-expression", path = "../expression", version = "2.0.1" } diff --git a/core/macros/Cargo.toml b/core/macros/Cargo.toml index eece5098..d427cc39 100644 --- a/core/macros/Cargo.toml +++ b/core/macros/Cargo.toml @@ -1,13 +1,14 @@ [package] -name = "zen-macros" -description = "Zen Helper Macros" +name = "phenixrizen-zen-macros" +description = "Zen Helper Macros (maintained fork of gorules/zen)" version = "2.0.1" edition = "2024" license = "MIT" -repository = "https://github.com/gorules/zen.git" -homepage = "https://gorules.io" +repository = "https://github.com/phenixrizen/zen.git" +homepage = "https://github.com/phenixrizen/zen" [lib] +name = "zen_macros" proc-macro = true [dependencies] diff --git a/core/template/Cargo.toml b/core/template/Cargo.toml index 0d92fbac..7040c98a 100644 --- a/core/template/Cargo.toml +++ b/core/template/Cargo.toml @@ -1,15 +1,18 @@ [package] -authors = ["GoRules Team "] -description = "Zen Template Language" -name = "zen-tmpl" +authors = ["Phenix Rizen ", "GoRules Team "] +description = "Zen Template Language (maintained fork of gorules/zen)" +name = "phenixrizen-zen-tmpl" license = "MIT" version = "2.0.1" edition = "2021" -repository = "https://github.com/gorules/zen.git" -homepage = "https://gorules.io" +repository = "https://github.com/phenixrizen/zen.git" +homepage = "https://github.com/phenixrizen/zen" + +[lib] +name = "zen_tmpl" [dependencies] -zen-expression = { path = "../expression", version = "2.0.1" } +zen-expression = { package = "phenixrizen-zen-expression", path = "../expression", version = "2.0.1" } thiserror = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/core/types/Cargo.toml b/core/types/Cargo.toml index 25750e10..c1ab0f9d 100644 --- a/core/types/Cargo.toml +++ b/core/types/Cargo.toml @@ -1,11 +1,14 @@ [package] -name = "zen-types" -description = "Zen Core Types" +name = "phenixrizen-zen-types" +description = "Zen Core Types (maintained fork of gorules/zen)" version = "2.0.1" edition = "2024" license = "MIT" -repository = "https://github.com/gorules/zen.git" -homepage = "https://gorules.io" +repository = "https://github.com/phenixrizen/zen.git" +homepage = "https://github.com/phenixrizen/zen" + +[lib] +name = "zen_types" [dependencies] ahash = { workspace = true } From ffa88189edcd0310d6d239bd5a5dd35bc5d1391d Mon Sep 17 00:00:00 2001 From: Phenix Rizen Date: Wed, 19 Aug 2026 14:24:55 -0500 Subject: [PATCH 4/4] fix(expression): honour TZ when resolving the local timezone `iana_time_zone::get_timezone()` reads the system zone from /etc/localtime and ignores the environment, so a process started with `TZ=UTC` still computed dates in the host's zone. Every other date implementation on Unix treats TZ as the override, and in a container that mounts the host's /etc/localtime it is the only way to pin the zone at all. This also makes test_dates_csv pass off a UTC host. It sets TZ=UTC itself, which until now did nothing: on a machine in America/Chicago, `d('2023-10-15')` evaluated to 2023-10-15T00:00:00-05:00 against an expected ...Z, so the test passed only where /etc/localtime already said UTC. CI runners are UTC, which is why it has been green. The zone is resolved once and cached, so ordering matters: standard.csv contains date expressions too, and whichever date-touching test ran first seeded the cache for the rest. Both now pin the zone, and the resolution is documented as first-use-wins so a future test does not reintroduce the race. Co-Authored-By: Claude Opus 5 (1M context) --- core/expression/src/vm/date/mod.rs | 19 +++++++++++++++++-- core/expression/tests/isolate.rs | 7 +++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/core/expression/src/vm/date/mod.rs b/core/expression/src/vm/date/mod.rs index 30246b96..f754299d 100644 --- a/core/expression/src/vm/date/mod.rs +++ b/core/expression/src/vm/date/mod.rs @@ -188,14 +188,29 @@ mod helper { use std::str::FromStr; use std::sync::OnceLock; + /// The local timezone, resolved once per process. + /// + /// `TZ` takes precedence over the system zone, which is how every other date implementation + /// on Unix behaves and is the only way to override the zone in a container that ships the + /// host's `/etc/localtime`. `iana_time_zone` reads that file and ignores the environment, so + /// without this a process started with `TZ=UTC` still computes dates in the host's zone. + /// + /// Resolution is cached, so `TZ` must be set before the first date operation - setting it + /// later in the same process has no effect. fn tz() -> Tz { static CACHED_TZ: OnceLock = OnceLock::new(); *CACHED_TZ.get_or_init(|| { - iana_time_zone::get_timezone() + std::env::var("TZ") .ok() + .filter(|tz| !tz.is_empty()) .and_then(|tz| Tz::from_str(&tz).ok()) - .unwrap_or_else(|| Tz::UTC) + .or_else(|| { + iana_time_zone::get_timezone() + .ok() + .and_then(|tz| Tz::from_str(&tz).ok()) + }) + .unwrap_or(Tz::UTC) }) } diff --git a/core/expression/tests/isolate.rs b/core/expression/tests/isolate.rs index 0412ca00..f1654c9c 100644 --- a/core/expression/tests/isolate.rs +++ b/core/expression/tests/isolate.rs @@ -917,12 +917,19 @@ mod test { #[test] fn test_standard_csv() { + // standard.csv contains date expressions, and the local timezone is resolved once per + // process. Whichever date-touching test runs first seeds that cache, so every one of + // them must pin the zone or the others inherit the host's. + env::set_var("TZ", "UTC"); + let csv_data = include_str!("data/standard.csv"); test_csv_standard(csv_data); } #[test] fn test_dates_csv() { + // See test_standard_csv: the zone is cached process-wide on first use, so this must be + // set before any date expression is evaluated, in this test or a parallel one. env::set_var("TZ", "UTC"); let csv_data = include_str!("data/date.csv");