Apache Texera: Scala/sbt backend services + the Amber workflow execution
engine, an Angular UI, and the agent service. JVM modules wired in
build.sbt.
| Area | Path | Detail |
|---|---|---|
| Workflow execution engine (Amber) | amber/ |
amber/README.md |
| Backend services | config-service/, access-control-service/, file-service/, computing-unit-managing-service/, workflow-compiling-service/, notebook-migration-service/ |
build.sbt |
| Shared Scala libs | common/ (auth, config, dao, util, workflow-core, workflow-operator, pybuilder) |
build.sbt |
| Frontend (Angular) | frontend/ |
frontend/README.md |
| Agent service (Bun/TS, LLM agents) | agent-service/ |
agent-service/package.json |
| Pyright language service | pyright-language-service/ |
pyright-language-service/README.md |
| Deploy scripts / Dockerfiles | bin/ |
README / k8s / single-node |
| DDL, sbt plugins | sql/, project/ |
files therein |
| Path | Role |
|---|---|
amber/src/main/scala |
Pekko actors, scheduler, reconfiguration, fault tolerance, gRPC/proto |
amber/src/main/python/pyamber |
Python engine (pyamber) — bridge to the Scala engine |
amber/src/main/python/pytexera |
Python operator SDK exposed to UDFs |
| Topic | Source of truth |
|---|---|
| Contribution / PR / lint / format / testing / license header | CONTRIBUTING.md |
| Reporting security issues | SECURITY.md |
| PR template | .github/PULL_REQUEST_TEMPLATE |
| Issue templates | bug / task / feature |
License-header coverage; vendored workflow-operator |
.licenserc.yaml; project/AddMetaInfLicenseFiles.scala |
| Run the local dev stack (infra in Docker; backend/frontend/agent-service native) | bin/local-dev.sh |
| Single-node / k8s deploy | single-node, k8s |
If a topic is above, read that file instead of asking here.
- Narrowly scoped changes. No unrelated rewrites or cross-service moves.
git status --shortbefore editing; don't revert unrelated dirty files.- Never commit secrets / local config / build output / caches / binaries
(
python_udf.conf,.env,target/,dist/,.pytest_cache/,.ruff_cache/, logs).
Leave texera/ on main. One worktree per PR, branched off a freshly
fetched upstream/main.
texera/ # stays on main, never dirty
texera-worktrees/<branch>/ # one worktree per PR
Reset to upstream/main at start; git log upstream/main..HEAD should
contain only this PR's commits before pushing; remove the worktree after
merge.
Prefer bin/local-dev.sh to run the stack while
developing. Its native services bind fixed ports and share one PID/state dir,
so only one worktree's stack runs at a time: bin/local-dev.sh down in the
old worktree before switching, then up in the new one. Use the
non-interactive CLI subcommands (up / down / status / logs); the
interactive TUI (-i) is for humans, not agents.
| Component | Version |
|---|---|
| Java | JDK 17 |
| Scala | 2.13 |
| Python | 3.12 |
| Node | 24 |
One Python venv shared across worktrees, sibling of the texera checkout:
<workspace>/
├── texera/ # main checkout
├── texera-worktrees/<br>/ # per-PR worktrees
└── venv312/ # shared Python 3.12 venv
python3.12 -m venv ../venv312 && source ../venv312/bin/activate
pip install -r amber/requirements.txt -r amber/operator-requirements.txt
# For pytest or running bin/python-proto-gen.sh, also install dev deps:
pip install -r amber/dev-requirements.txtTests that spawn Python workers need an interpreter path. Edit python.path
in udf.conf or
export UDF_PYTHON_PATH="$(pwd)/../venv312/bin/python" (env var overrides).
Without it, sbt Python-integration tests fail to launch a worker.
.jvmopts holds every --add-opens flag Texera needs for
JDK 17+, with each group annotated by its upstream source (Kryo,
Apache Arrow, Apache Pekko). sbt's launcher and the .run/
configs read it automatically; for raw java launches, pass it as an
argfile: java @.jvmopts -jar …. If a future library version or a new
code path triggers an InaccessibleObjectException, add the open to
.jvmopts. project/JdkOptions.scala
will propagates the changed options to forked test JVMs, sbt-native-packager dist launchers,
and IntelliJ.
Short, Conventional Commits, same shape for branch and commit subject.
| Kind | Branch | Commit |
|---|---|---|
| Feature | feat/agent-workflow-edit |
feat(agent-service): enable workflow edit |
| Bug fix | fix/marker-replay |
fix(amber): marker replay during reconfiguration |
| Tests | test/pyamber-handlers |
test(pyamber): add handler unit tests |
| Chore | chore/angular-21 |
chore(deps, frontend): upgrade to Angular 21 |
| CI | ci/merge-queue-stacking |
ci: stack merge-queue builds by module |
Both ≤ ~60 chars. For code changes, if you use a scope, use the module name
(amber, pyamber, frontend, agent-service, file-service, …) — not
amber-python. No Co-authored-by: trailer for the repo owner.
Choosing the type turns on what happens to the behavior, not on how big the diff is:
| The change | Type |
|---|---|
| Worked before, broken now | fix |
| Support never existed; adding it | feat |
| Support exists; removing it | feat |
| Reworked so user-facing behavior intentionally changes | feat |
| User-facing behavior unchanged | refactor |
Behavior is what the code does, not what a doc or an old PR description claims
it does: implementing something that was never actually there is a feat.
refactor claims the user-facing behavior is identical. Tests that pin a
user-facing API must pass untouched — editing one of those assertions means
the behavior moved, so it is a feat or a fix. Tests that pin internals (a
private helper's signature, call order between collaborators, the shape of an
intermediate value) mirror the implementation, so rewriting them alongside the
code they mirror is still a refactor.
Tests. A test-only PR is test(<module>): .... Repairing a broken or
flaky test is a bug fix in test code: fix(test, <module>): ....
Dependencies. fix only when the bump carries a security fix:
| Bump | Commit |
|---|---|
| Patches a CVE | fix(deps, <module>): ... |
| Everything else | chore(deps, <module>): ... |
| GitHub Actions | chore(deps, ci): ... |
Omit the module for cross-module bumps (sbt). GitHub Actions bumps take ci
as their module — that is what .github/renovate.json5
opens them with; a bare ci: ... is for hand-written CI and workflow changes.
Backports. A PR targeting release/vX.Y appends the version as the last
scope component — fix(deps, frontend, v1.2): .... Version tags belong only
on release-branch PRs, never on one targeting main.
Issue-first; both stay short.
issue (template + Type) -> /take -> PR (Closes #N, template) -> review -> merge
-
Every change starts as an issue (minor typo / docs excepted). File against
apache/texera, never a fork. -
Pick the right template and set the GitHub Issue Type explicitly (
Bug/Task/Feature); the template'stype:frontmatter doesn't always apply on creation. -
Claim before coding. The assignee is the claim; a local branch is not. An issue is taken if anyone but you is assigned, or a linked PR is
OPENand someone else wrote it (an outside PR opener isn't always auto-assigned):gh api user --jq .login # you gh issue view N --repo apache/texera --json assignees,closedByPullRequestsReferences gh pr view P --repo apache/texera --json author,state # each linked PR
Not taken:
/takeit, even one you just filed (comment commands;/untakeif you drop it). Taken: it's theirs. Don't start; tell the human. -
Reference the issue:
Closes #N(orFixes/Resolves, or "related to"). Right before opening the PR, re-run the check for every issue your diff fixes, closed or only related, including each site of a bundled PR. If one is taken, leave that issue and its code change out and tell the human: merging resets each closed issue's assignees to the PR's authors (pr-assignment.yml). -
Issue titles are plain prose; never use the Conventional Commits format (
type(scope): ...) — that prefix is for commit and PR titles only. -
Task issues match
task-template.yamlexactly. -
Prefer tables and small ASCII diagrams over long bullets. Don't restate the diff or the template.
-
For bugs, lead with root cause and a before -> after sketch:
Before: reconfiguration -> replay marker -> worker hangs After: reconfiguration -> replay marker -> resume from checkpoint -
Frontend PRs: any visible UI change requires screenshots / GIF, before / after side by side. For purely visual fixes that's the primary verification under "How was this PR tested?"; interactive flows also list manual steps (click path, browser, viewport).
TDD. Write the test before the source change.
write/adjust test (red) -> edit source (green) -> refactor
| Situation | Order |
|---|---|
| New feature / behavior change | Failing test, then implement. |
| Bug fix | Regression test reproducing the bug, then fix. |
| Code with no tests | Characterization tests pin current behavior first; only then change source. |
| Refactor (no user-facing behavior change) | Tests stay green throughout. User-facing API assertions stay untouched; tests that mirror internals may be rewritten with the code. |
Every test must cover:
- Both directions: positive (valid → expected) and negative (invalid / error → specific failure mode).
- Edge cases: empty / null / zero / max / boundary, unicode, concurrency/order, missing or malformed config.
- Don't assume valid. External input (user / API / file / message) must be tested with bad input.
Don't claim "tested" without commands. Paste the exact sbt testOnly /
pytest / yarn test:ci / bun test invocation under "How was this PR
tested?".
CI runs are selected by PR labels, not by file diff.
diff -> pr-labeler -> labels on PR -> required-checks maps labels to stacks -> CI runs
- Path → label rules:
.github/labeler.yml - Label → stacks (
LABEL_STACKS, source of truth):.github/workflows/required-checks.yml. Read it directly; don't duplicate the mapping here. - Need extra coverage the diff doesn't imply (e.g. a
common/change you suspect breaks the frontend)? Add the relevant label manually. - Empty stack union (docs-only / dev-only /
dependencies/feature/fix/refactor/release/*only) skips every build stack on purpose. release/*labels nominate backport targets. A nominated target is backported only once that branch's release manager — listed in.github/release-branches.yml— approves the PR, and the requiredBackport Approvalscheck blocks the merge until everyrelease/*label on the PR is approved. A manager declines by removing their label, so the labels on a merged PR are exactly the branches it was backported to.