Language rules
What each language MUST satisfy. We do not mandate a language for a service. Shell is only glue (SHELL-1).
Go
- Layout and naming: Go style guidelines.
- The
mise.tomland the config root: BUILD-6. - The task names: BUILD-4.
- The Dockerfile: BUILD-3 and BUILD-16.
GO-1: lint MUST pass
These are defined in the Go lint mise task.
DO NOT express per-project strictness by redefining the task.
Enforcement: //<project>:lint.
GO-2: Integration tests live in testsintegration/
- An integration test MUST NOT run under
-short. - You MUST use the
TestMaintemplate in the style guide. test-fastMUST stay fast.
Enforcement: //<project>:test-fast behaviour. Review only otherwise.
GO-3: Depend on daedalus by module path, with replace
You MUST write require lyceum.technology/daedalus v0.0.0 plus a replace to ../daedalus_go. Module naming is in
the Go style guidelines.
Enforcement: Review only.
Python
There is no separate Python style guide. These rules and ruff's configuration are it.
PYTHON-1: Every Python project MUST be a uv workspace member
You MUST list it in the root pyproject.toml [tool.uv.workspace].
A non-member cannot share the lockfile and cannot be built by the shared Python service image.
Enforcement: uv-lock (pre-commit) for the lockfile. Review only for membership.
PYTHON-2: One Python version for the whole repository
- You MUST pin it once in the root
.python-version. - DO NOT add a per-project pin.
uvreads.python-versionas an exact request.
Enforcement: //infra/docker:check-image-pins (pre-commit) for the image side. Review only otherwise.
PYTHON-3: Use the repository ruff configuration unmodified
lintMUST runruff format --checkandruff checkwith the full configuration.- DO NOT hand-pick a subset.
- Both MUST run from the repository root with the project as an explicit target.
ruffresolvessrcagainst the working directory.
Enforcement: //<project>:lint, which does the cd for you.
PYTHON-4: Python MUST type-check
- New code MUST pass the repository type checker in strict mode. The root
pyproject.tomlconfigures it. appcarries a baseline that MUST only shrink.- A cleaned file MUST NOT regress.
Enforcement: //<project>:lint runs ty, in the sweep. See
enforcement gaps.
PYTHON-5: No tox
- DO NOT add a
toxenvironment. - Tasks MUST call
uvdirectly.--only-groupand the interpreter pin give the isolation. toxwould be a second task runner besidemise, with its own environment list to keep in step.
Enforcement: Review only.
PYTHON-6: Tests live in the project's tests/ directory
//<project>:test-fast runs tests/. It skips cleanly where there is none.
Enforcement: mise/scripts/py-test.bash.
PYTHON-7: A repository script lives in mise/scripts/
- A script MUST be
mise/scripts/<name>.py. Its tests MUST bemise/scripts/tests/test_<name>.py. - It MUST use the standard library only. If it needs a dependency, then it belongs in a workspace package such as neph.
- It MUST run without Mise as
uv run --no-project python mise/scripts/<name>.py. Its Mise task runs that. - It MUST import shared code from
mise/scripts/lib.pyas a sibling module:import lib. - It MUST run every command through
lib.run. A test replaces that one function.
mise/scripts/ is the lyceum-mise-scripts workspace member and a Mise config root. It is never built.
Enforcement: //mise/scripts:lint and //mise/scripts:test-fast, in the sweep (check-lint-and-test.yml). Review
only for the rest.
Shell
Shell is glue. A script is Python.
SHELL-1: Shell is glue. A script MUST be Python
- Shell glue MUST be at most a few lines of simple commands, with at most one control statement.
- Glue MAY pipe through simple filters such as
grep -F,cut,sortandhead. - Anything more is a script. A script MUST be Python (PYTHON-7).
- Complicated
sed,awk,jqoryqMUST NOT be used in shell. - A regular expression MUST NOT be used in shell. A pattern MUST be a glob or a fixed string. A regular expression in shell cannot be unit-tested.
- Shell MUST NOT hold data structures, arithmetic or error recovery.
- This binds every shell file, every workflow
run:step and every Mise taskrun. - An existing shell script MAY take a small fix. A change that adds logic to it MUST port it to Python.
Enforcement: Review only.
SHELL-2: Every shell file names its interpreter and passes shellcheck
- A file MUST be
.bashor.sh, withset -euo pipefailat the top. - It MUST be clean under the repository
.shellcheckrc. - A sourced file keeps its shebang and exec bit, so
check-shebang-scripts-are-executablepasses.mise/scripts/go-env.bashis the model.
Enforcement: //mise:lint for mise/scripts/. Review only elsewhere. See
enforcement gaps.
SHELL-3: Scripts MUST be runnable without Mise
- You MUST configure a script by environment variables with documented defaults.
- A Docker stage MUST be able to call one directly.
Enforcement: Review only.
SHELL-4: Avoid the four shell traps
- DO end a function whose last statement is a conditional with
return 0. Underset -ea falseifaborts the caller. - DO add
|| trueto agrepin a command substitution where you handle the empty case yourself. Underpipefaila non-match aborts the script. - DO NOT use
sed -i. Usesed_inplacefrommise/scripts/lib.bash. GNU and BSD spell-idifferently. - DO check a tool's docs before claiming a variable name, and prefix ours. Docker reads
DOCKER_CONTEXTitself.
Enforcement: shellcheck, for some. Review only for the rest.
SHELL-5: Every shell script MUST be compatible with both Linux and MacOS
- You MUST write shell scripts compatible with both Linux and MacOS.
Enforcement: Review only.