


Patch a Long-Lived Portable C Project Without Breaking Compatibility
Use when an AI changes a mature C codebase that must keep building on old compilers, many platforms, and optional feature sets. It builds and tests the affected setup, follows the style, memory, commit, docs, and release rules, and keeps each patch small and valid on its own.
To use this AI rule
## Check which platforms and feature builds must keep working before a change
- Treat portability, stability, robustness, and backward compatibility as primary design constraints.
- Account for builds ranging from a minimal standards-compatible configuration to a full graphical configuration with optional scripting interfaces.
- Read the repository's high-level design policy before changing architecture or public behavior.
- Distinguish core source, runtime assets, translations, tests, documentation, subprojects, and vendored upstream code.
- Do not assume the newest compiler, operating system, feature set, or graphical environment.
- Keep behavior coherent across supported variants unless the change is explicitly feature-gated.
## Build and test the affected configuration with no new warnings
- Use the documented build path for the current platform and toolchain.
- Regenerate function declarations or other derived interfaces through their generator rather than editing generated output by hand.
- Run the focused test file during iteration and inspect both the message output and the detailed test log.
- Before submission, require a clean build with no new warnings, passing relevant tests, and source style matching the edited file.
- When platform build procedures differ, consult that platform's installation guide instead of translating commands by guesswork.
- Expand to broader tests when shared code, feature combinations, or runtime files are affected.
## Regenerate generated files from their generator and respect file owners
- Learn which directories are generated, imported from upstream, shared with related projects, or maintained by a designated owner.
- Change a generated file only when its source changes, and keep unrelated regeneration out of functional patches.
- Route maintained runtime changes through the documented owner workflow rather than bypassing ownership.
- Update required change dates or descriptions when editing shipped runtime and help files.
- Add the repository's required patch-tracking entry for core source changes, except where an explicit directory exception applies.
- Never mix vendor updates, generated noise, translations, and unrelated source cleanup into one patch.
## State the problem, not the fix, in the commit subject
- Follow the repository's exact patch subject and body structure.
- State the problem in the subject, not the implementation of the solution.
- Write a complete problem sentence and a concise solution sentence with the required attribution and punctuation.
- Put optional explanation after the required problem and solution header, using prose rather than bullet lists when the format requires it.
- Label fixed, closed, and related references according to their distinct meanings.
- Include the required developer certification, and acknowledge AI assistance only through the accepted coauthor mechanism.
## Match the existing C style and avoid syntax old compilers cannot handle
- Preserve four-space logical indentation while respecting file-specific tab or spaces-only rules.
- Place opening braces on their own lines for functions and control flow.
- Format function definitions with the return type and function name in the repository's established layout.
- Match the surrounding file's declaration placement instead of modernizing it opportunistically.
- Use only the documented baseline C language plus its approved newer features; do not introduce constructs unsupported by legacy compilers.
- Run any file-specific formatter after edits and review its diff.
## Use the project's types and pass known string lengths to helpers
- Use the repository's custom type suffix and avoid suffixes reserved by system standards.
- Preserve an ongoing boolean migration: use the locally established boolean form consistently within a patch and avoid mass conversions.
- Initialize locals when first assignment is hard to see, but do not add redundant initialization that can conceal an uninitialized-read defect.
- Use the literal-length helper for string literals and the length-returning formatter when the written size is needed.
- When a string length is already known, pass it to the length-aware operation instead of scanning again.
- Keep each byte-buffer pointer adjacent to its `size_t` length; use narrower integer types only where a legacy interface requires them.
## Handle memory through the project's wrappers and free it on every error path
- Prefer repository wrappers over raw standard-library allocation, deallocation, copy, move, character, and whitespace operations.
- Choose wrappers for their documented null handling, out-of-memory behavior, overlap safety, encoding behavior, and platform consistency.
- Check a divisor explicitly before every division, even if an earlier compound condition appears to imply nonzero.
- For every new allocation, verify cleanup on success, early return, and each error branch.
- Keep buffer sizes in bytes and make truncation, termination, and written-length behavior explicit.
- Do not replace a safer project wrapper with a generic library call merely because the generic call is familiar.
## Follow naming and macro rules, and keep makefiles portable
- Follow reserved-name restrictions, forbidden identifiers, function-name length limits, spacing, and one-statement-per-line rules.
- Use the established feature prefix and uppercase macro convention.
- Test feature macros with the repository's supported conditional pattern.
- Keep main build files within the documented portable make dialect; avoid implementation-specific assignment, pattern, shell, and conditional extensions.
- Reuse the project's string and byte types and their corresponding macros.
- Read the authoritative style references before introducing a new identifier, macro, or build rule.
## Write runtime scripts that also work in related runtimes and forks
- Use the current script style for new files while considering compatibility with related runtimes and forks.
- When compatibility is uncertain, ask before adopting a newer language construct.
- Follow local-variable naming conventions in script tests.
- Do not repeat a feature gate inside individual tests when the file already gates that feature.
- If the file has no top-level gate, add a focused gate only to tests that require the optional feature.
- Keep runtime-script changes separate from unrelated core-language modernization.
## Write regression tests that reproduce the reported bug
- Prefer a deterministic assertion over a test that merely verifies the program does not crash.
- For rendering defects, add a screen-capture comparison; for incorrect data, assert the exact output.
- Add comprehensive coverage for new behavior, preferably in an existing relevant test file.
- When fixing a reported defect, reproduce the original scenario rather than only a simplified synthetic case.
- Use the standard helpers for terminal sessions, screen dumps, script-load success, and expected script-load failure.
- Put syntax and indentation runtime tests in their dedicated suites.
## Update the help docs in the same patch and keep their format
- Update user-facing documentation in the same patch as any option or feature change.
- Refresh the help file's change header when editing existing documentation.
- Preserve the authoritative help format for file headers, modelines, width, tab alignment, unique tags, cross-references, headings, code examples, and highlighted notation.
- Use the safe retab operation and review its diff; do not force a broad whitespace rewrite.
- Keep link tags globally unique and prefix extension-specific tags when needed.
- Prefer gender-neutral wording in new or modified text without rewriting unaffected text solely for that purpose.
## Check the release phase first, and keep deprecated behavior reachable
- Determine whether the project is in a development cycle or a stability period before proposing scope.
- During a stability period, limit accepted changes to clear defects, security fixes, documentation, translations, and runtime maintenance.
- Do not introduce new features or backward-incompatible core changes during stability.
- Preserve released core behavior across minor versions.
- Keep deprecated behavior reachable through configuration, document it as deprecated, allow compile-time disabling where supported, and remove it only in a later permitted cycle.
- Treat runtime assets according to their documented maintenance flexibility without weakening core guarantees.
## Submit patches where every commit builds and passes tests on its own
- Read the security policy before reporting a suspected vulnerability or changing security-sensitive code.
- Ensure the commit format, clean build, relevant tests, regression coverage, documentation, and certification are all complete.
- Keep the diff limited to the stated problem: no stray whitespace, unrelated refactors, or unrelated generated output.
- In a multi-patch series, require every commit to compile and pass its own tests.
- Do not leave an intentionally broken intermediate commit for a later commit to repair; combine them instead.
- Confirm that documented behavior and enforced behavior still agree, and describe intentional changes in the problem and solution record.
## Make the smallest change, and ask before acting outside the working tree
- Make the smallest change that achieves the goal and match the surrounding code instead of imposing a broad improvement.
- Split a patch that solves multiple independent problems.
- If a patch addresses only a symptom, state the deeper issue and the chosen scope limitation.
- Reproduce a defect before claiming it exists, and read the implementation before claiming what it does.
- Do not rely on remembered file contents when current source is available.
- Confirm with the user before installing packages, modifying files outside the working tree, pushing branches, or using network operations.
