This file is for coding agents, and for the humans driving them, working in this repository.
It is a digest of doc/contribute/ plus the things CI and maintainers reject most often. Where
it is silent or disagrees with the documentation, the documentation wins:
doc/contribute/guidelines.rst (process, DCO, AI-assistant policy),
doc/contribute/contributor_expectations.rst (PR shape), doc/contribute/style/ (C, Kconfig,
CMake, Doxygen and devicetree style) and doc/contribute/coding_guidelines/index.rst (the
MISRA-C subset). Read the relevant page before working in an area you have not touched before;
do not rely on this digest or on training data alone.
- Never add a
Signed-off-by:line: only the human submitter may sign off (DCO). Never addCo-authored-by:. Do not put "Generated with ...", session links or any other mention of AI tools in commit messages, PR bodies, issues or comments. TheAssisted-by:trailer below is the only place AI involvement is recorded. - Add exactly one
Assisted-by: <Agent>:<model-version> [tool ...]trailer, for exampleAssisted-by: Claude:claude-opus-4.6 coccinelle, naming the tool actually used. Replace it rather than stacking when a different model amends the commit.checkpatch.plvalidates the format; basic tools (git, gcc, cmake, editors) are not listed. - The human reviews and tests every change before it is submitted. State what you did not do (not built, not run, not run on hardware, no reproducer) instead of implying it was done.
- Review comments written with AI help are verified by the human before posting; never post raw
model output (
doc/contribute/reviewer_expectations.rst). - Commit messages, code comments, Kconfig help texts and docs describe the tree as it is: no references to prompts, plans, sessions, "the rework" or "as requested". Write tersely and concretely; maintainers reject verbose, hedging or self-congratulatory prose. No emoji.
- Change what the task needs, which can mean refactoring the code a fix touches, but keep unrelated reformatting, renames and cleanups out of the change; style is enforced on new or modified lines only. A wider cleanup is a separate PR.
- Zephyr builds only inside a west workspace: from the parent directory of the clone,
west init -l <zephyr-dir> && west update(most boards need HAL and module repositories fromwest.yml). Build withwest build -b <board> <app-dir>;native_simruns on the host. Seedoc/develop/west/workspaces.rst. - Tests and samples are run by twister:
west twister -p native_sim -T <test-dir>or-s <test-dir>/<scenario-id>;--build-onlyskips execution;-iprints failing logs. Suites whosetests.yamllistsunit_testingas platform need-p unit_testing. Seedoc/develop/twister/index.rst. - In a
git worktree, exportZEPHYR_BASE=<worktree>forwest buildand twister; otherwise the workspace's registered checkout is built silently instead of your tree. - Every commit in a series must build and pass its tests on its own (bisectability).
CI runs the same checks (.github/workflows/compliance.yml); each failure costs a full round
trip. Fix the cause, never work around a check.
pip install -r scripts/requirements-compliance.txt, then./scripts/ci/check_compliance.py --parallel -c upstream/main..HEAD(upstreambeing the zephyrproject-rtos remote,originin a plain clone; the same below).-llists the checks (Checkpatch, Gitlint, KconfigBasic, CMakeStyle, DevicetreeBindings, Ruff, Pylint, YAMLLint, SphinxLint, KeepSorted, ...);-m <Check>runs one.- checkpatch on one commit:
git format-patch -1 --stdout <sha> | ./scripts/checkpatch.pl -. Pipinggit showinstead produces bogusBAD_SIGN_OFFerrors. - ClangFormat is advisory (
.clang-format). Apply it to your own hunks only and skip hunks where it reflows surrounding macro tables; never run it on whole files. - CMake style (
doc/contribute/style/cmake.rst) is checked on touched lines only and old files are grandfathered: copying an oldCMakeLists.txtas a template can fail CI although the original passes. The same holds for any in-tree file you copy: check it against the current style page, not only against its neighbors.
area: subarea: imperative summary (75 columns max, no trailing period)
Body wrapped at 75 columns: what the change does, why this approach,
which assumptions were made and how it was tested. Never empty.
Fixes #12345
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Full Name <email@example.com>
area:is the prefix the file's recent history uses:git log --format=%s -20 -- <path>. Examples:Bluetooth: Host:,drivers: i2c: nrfx:,dts: arm: st:,boards: nordic:,kernel:,doc:,.github:. Neversubsys:ortreewide:, neverWIP.- Trailers (
Assisted-by,Signed-off-by,Link:,(cherry picked from commit ...)) form one block as the last paragraph.Fixes #Ngoes in its own paragraph directly before that block. Reference other commits ascommit <12-char sha> ("subject"). - One
Signed-off-by:line must match the commitAuthor:name and e-mail; never remove an existing sign-off. Verify withgit log -1 --format='%an <%ae>%n%b'. - One logical change per commit: a fix, its tests and its docs may be separate commits, but one
commit never mixes unrelated changes. No fixup, squash or merge commits in a PR: fold review
fixes into the commit that owns the lines. Without an interactive terminal:
git commit --fixup=<sha>, thenGIT_SEQUENCE_EDITOR=true GIT_EDITOR=true git rebase -i --autosquash <base>. - Update a branch with
git rebase upstream/main, never by mergingmaininto it. - Stage explicit paths (
git add <file>); nevergit add -A,git add .orcommit -a, which pick up build directories, notes and reports. Before pushing, reviewgit show --stat upstream/main..HEAD.
- A PR is one self-contained change. Reviewers are assigned from the
MAINTAINERS.ymlareas of the touched files (./scripts/get_maintainer.py path <files>lists them), so when a change spans several areas and splits cleanly, split it so each part gets the right reviewers. Small PRs get reviewed; large changes start as an RFC issue (doc/contribute/proposals_and_rfcs.rst). - The PR description summarizes the change and its rationale and carries the
Fixes #Nline when the change addresses an issue. It is rendered as Markdown: unwrap paragraphs instead of pasting hard-wrapped commit text. Keep title and description in sync with the commits after every force-push. - Breaking changes to stable APIs are described in the migration guide; new, deprecated and
removed APIs are listed in the release notes. Both go in the same PR as the
change:
doc/releases/migration-guide-X.Y.rstanddoc/releases/release-notes-X.Y.rst, whereX.Yis the next release (VERSIONshowsX.(Y-1).99during development). Stable APIs followdoc/develop/api/api_lifecycle.rst. - Backports to
v*-branchare opened by a bot when a maintainer addsbackport vX.Y-branchlabels after merge. Do not open manual backport PRs unless the bot failed. Every backport PR body must contain aFixes #Nline resolving to a real issue (.github/workflows/backport_issue_check.yml), so bug fixes that may be backported need a public issue. - Issues use a GitHub issue type (Bug, Feature, Enhancement, RFC, Task) and the templates in
.github/ISSUE_TEMPLATE/, not abuglabel:gh issue create --type Bug .... - Security: never file a public issue or PR describing an undisclosed vulnerability, and never
reference an unpublished advisory (GHSA/CVE) in a commit, PR or issue. See
.github/SECURITY.md. - When reviewing, remember that CI tests the PR rebased onto current
main. Reproduce the same way before reporting a failure or claiming whatmaindoes or does not contain:git fetch upstream main && git fetch upstream pull/<N>/head && git checkout --detach FETCH_HEAD && git rebase upstream/main(two fetches: after a multi-ref fetchFETCH_HEADis the first ref,main).
Complete lists: doc/contribute/style/code.rst, doc/contribute/coding_guidelines/index.rst.
- Linux kernel style: 8-column tabs, 100-column lines, braces on every
if/else/loop body,/* */comments only (no//),snake_case, no binary literals; non-ASCII symbols only where they significantly improve clarity, emoji never. - Explicit comparisons:
if (err != 0),if (ptr == NULL),if (len > 0). Controlling expressions must be essentially Boolean (coding guideline rule 85);if (!err)on an integer is not allowed. - Fixed-width types (
uint8_t,int32_t) for numeric data andsize_tfor sizes instead of bareint,shortorlong(Dir 4.6;charstays for text);Usuffix on unsigned constants; no octal constants. - No commented-out code; check every return value that carries an error; include guards in every header.
- Dynamic allocation: the coding guidelines list MISRA Dir 4.12 (no dynamic allocation) and
Rule 21.3 (no
<stdlib.h>allocators) as required, while Rule A.4 allows the libc allocators in the kernel and subsystems usek_malloc(),k_heap, slabs andnet_bufpools. Follow the surrounding subsystem: do not "fix" existing allocation, and do not add a heap where a static pool fits. - Prefer
if (IS_ENABLED(CONFIG_FOO)) { ... }to#ifdef CONFIG_FOOfor code paths. Both branches must compile, so declarations in headers stay unconditional (guideline Rule A.1). Use the preprocessor only for real memory or ABI differences, and follow the surrounding file's pattern for small edits. - Public symbols carry their subsystem prefix (
k_,sys_,net_,bt_,i2c_, ...). Public functions have Doxygen with a brief description,@paramand@retval <value> <text>for each discrete return value, success first and a value repeated when different conditions produce it:@retval -EINVAL Invalid argument, not "if the argument is invalid" (doc/contribute/style/doxygen.rst). - Every
CONFIG_symbol referenced in code or Kconfig must be defined in the tree (KconfigBasic check). Symbol naming andmenuconfig/ifstructure:doc/contribute/style/kconfig.rst;selectversusdepends on:doc/build/kconfig/tips.rst. - New original files start with the two SPDX header lines in the file's comment syntax: the
copyright text (
Copyright The Zephyr Project Contributors, or the actual holder) and theApache-2.0license identifier; the top of this file shows the Markdown form. Code imported under another license keeps its own headers, needs the approval described indoc/contribute/guidelines.rst(Components using other Licenses) and an annotation inREUSE.toml; never rewrite existing license metadata. - Test and sample metadata lives in
tests.yaml;sample.yamlandtestcase.yamlare legacy names CI rejects for new files. Scripts that twister executes directly, such as bsimtests_scripts/*.sh, must be committed with mode100755. - Blocks delimited by
zephyr-keep-sortedstart/stop marker comments stay sorted (KeepSorted check). No binary files except images underdoc/,boards/andsamples/within the size limits (the BinaryFiles check holds the exact allow-list). - Devicetree: a compatible for a vendor's device is
<vendor>,<device>with the vendor listed indts/bindings/vendor-prefixes.txt; generic hardware such asgpio-ledsgets no invented prefix. Every new compatible needs a binding underdts/bindings/. Seedoc/contribute/style/devicetree.rstanddoc/build/dts/bindings-upstream.rst. - Documentation under
doc/is reStructuredText in American English, 100 columns (URLs excepted):doc/contribute/documentation/guidelines.rst.
- Subsystems:
subsys/; drivers:drivers/<class>/; public API headers:include/zephyr/and the APIs defined underlib/(a subsystem's owninclude/zephyr/, such as ztest's, is not public API); kernel:kernel/; boards:boards/<vendor>/; SoCs:soc/; devicetree:dts/; tests:tests/; samples:samples/; tooling:scripts/. MAINTAINERS.ymlmaps paths to areas and people. The recent history of a path is the best guide to its local conventions:git log --oneline -20 -- <path>.- The documentation source is
doc/, published at https://docs.zephyrproject.org/latest/. The published docs, the source tree and the last six months of issues and PRs are also searchable through the project's MCP server,https://zephyrproject.mcp.kapa.ai(doc/develop/tools/kapa_ai.rst); its answers are generated and must be verified against the tree.