Contributing to aslice
aslice keeps deprecated Intel Macs (macOS 10.11–12, x86_64) useful. Contributions land in three places, in rough order of what the project needs most:
- The orchards — packages. New formulae, version bumps, patches that keep old software building on old OS releases, better
tests.starsmoke tests. This is the daily work and the easiest way in. - The farm — build time. Spare Intel Macs can enroll as community evidence builders (
aslice farm enroll+--reproduce-only): your machine rebuilds published jobs and reports digest agreement. Your machine runs the same code as the official farm, but its results carry no publication authority (BUILD-INFRA §7.4). - aslice itself — the manager is C++20. Talk in an issue before writing large features; the design docs (
docs/DESIGN.mdand companions) are the spec, and PRs that contradict them need to win the argument first.
Project terms, acronyms, and the Homebrew translation table: docs/NOMENCLATURE.md.
Ground rules (read these before your first PR)
- No telemetry, ever. Do not submit code that phones home, counts users, measures engagement, or "anonymously" reports anything. This is a charter decision, not a preference; PRs adding metrics plumbing are closed on sight. (The farm measures itself — build times, queue depth, reproducibility coverage — and that is the only instrumentation that exists.)
- Zero undeclared install-time code. Packages never execute code at install — no
post_install, and no installer scripts unless declared as a graft: hash-pinned, approved by the user per package, sandboxed to a declared behavior manifest, farm-rehearsed for core and extended (DESIGN §12.15). Vendor.pkg/.dmgsoftware is payload-only extraction by default. This is the security model; outside a declared graft there is no "just this once." - No sudo in steady state. If your change needs root outside the declared
aslice-systempaths (kexts, SIP-off dev tools, system-domain services,[system-patch]), the change is wrong. - The platform is the platform. 10.11–12, x86_64. PRs for Apple Silicon, macOS 13+, or Linux are out of scope by charter, however good they are.
Writing a formula
Target changes at develop (dev). Staging uses beta and production uses master, all in the same repository and its mirrors. This applies to aslice and third-party orchards as well as core and extended. Build and freeze a candidate once, then promote its identical artifacts through staging to prod. Do not hardcode environment-specific configuration or edit payloads to change version suffixes. See ORCHARD-POLICY §18.1 for branching and ORCHARD-POLICY §18.2 for x.y.z-develop → x.y.z-beta → x.y.z, required bumps, and promotion-failure reports.
Read PACKAGE-FORMAT §3 and PACKAGE-FORMAT §6 for the format, then docs/ORCHARD-POLICY.md for the acceptance rules. Reviewers check the following:
min_osaccuracy. Declare the oldest OS you are prepared to stand behind. CI builds at that floor and smoke-runs every release up to 12 on the VM matrix — optimism does not survive that gauntlet. Patch heroics land as patches, and the floor is lowered only when CI proves it (ORCHARD-POLICY §4).- Variant discipline.
abi = truevariants carry no cap: name the exported interface the variant changes, and review checks the tag against the ABI scan — not the worthiness of the need. ffmpeg-class packages legitimately carry many codec combinations, uncommon ones included; what a user enables is the user's call.abi = falsevariants are free. Defaults serve ~95% of installs, securely — TLS on, deprecated protocols off, even when upstream defaults differ (ORCHARD-POLICY §5). - Patches carry headers. Every patch file documents its origin (upstream commit/PR/issue URL, or the distro it was borrowed from, credited), what it fixes, and its removal condition ("drop when upstream ≥ 7.2"). Undocumented patches are a lint error. Patches fix builds, portability, and security — never features; a feature patch is a fork wearing a trench coat (ORCHARD-POLICY §7).
- Documentation standards. One-line
description(no leading article, no marketing, ends with a period), SPDXlicense, reachablehomepage,keywordsthat help search, andnotesonly when a user genuinely needs post-install guidance.tests.starmust prove the installed artifact works — link against the library, run the tool on real input;--versionis a last resort and says so in a comment (ORCHARD-POLICY §14). [livecheck]is required in core. If upstream releases software, the formula says how to detect it (ORCHARD-POLICY §9).- Dependencies resolve to aslice packages. Leaning on
/usr/libdylibs or/usr/bintools is rejected — on this platform the system libraries are the problem. The exceptions are macOS frameworks on the allowlist (Accelerate, SystemConfiguration, CoreAudio, …), enumerated in DESIGN §13.1.
The local loop
aslice create <url> # scaffold a formula from a tarball
aslice lint ./my-formula # schema + policy + --new-package rules
aslice build ./my-formula # full sandboxed pipeline, the same one CI runs
aslice build ./my-formula --keep --shell # drop into the sandbox at the failed phase
aslice test my-formula # tests.star against the installed result
Local and farm builds use the same pipeline, but host capability and OS coverage still differ. orchard ci reports tests deferred to the farm; a local pass alone does not prove the complete matrix.
Porting from Homebrew
The Ruby-formula importer is planned for tools/ (DESIGN §13.3). It will handle the mechanical translation; you handle the judgment calls:
| Homebrew | aslice |
|---|---|
keg_only |
link = false + link_reason (PACKAGE-FORMAT §3.8) |
caveats |
notes — actionable only |
post_install |
does not translate — declare [service], use notes, or rethink; a vendor installer script the payload cannot replace is declared as a graft (PACKAGE-FORMAT §3.11) |
uses_from_macos |
rejected, except allowlisted frameworks |
deprecate!/disable! |
[deprecation] table (PACKAGE-FORMAT §3.14) |
livecheck block |
[livecheck] table (PACKAGE-FORMAT §3.15) — usually near-mechanical |
plist_options / service |
[service] table (generated plist) |
MacPorts and pkgsrc patches for 10.11-era portability are fair game — credit them in the patch header.
The review process
Merge gates are mechanical (ORCHARD-POLICY §10): lint → sandboxed build on every declared flavor → smoke-run on every OS in [min_os, 12] → ABI gate for provider bumps → graft rehearsal for graft-bearing binaries, with dependent rebuilds published in the same atomic snapshot. Malware checks and required independent rebuilds remain publication gates; missing capacity leaves releases pending. During single-owner launch, the owner's merge is the final human release approval. The publisher automatically delivers an authenticated candidate to the dedicated networked release Pi, verifies returned signatures, and activates the complete release atomically (KEY-RUNBOOK §2.1). Clients discover it at metadata refresh; publication does not install it.
Owner approval applies to all changes during single-owner launch, including new core packages, versioned lineages, abi = true variants, system-software and system-patch packages, and policy changes. The owner may approve their own changes; second-reviewer and maintainer-vote requirements are superseded for this phase (ORCHARD-POLICY §17). Independent maintainers can help review; multi-party governance remains a future transition. Automated gates cannot be waived.
C++ changes
To check a change locally, configure a Debug build, build it, and run the same format and analysis targets used by CI:
cmake -S . -B build/check -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build/check
cmake --build build/check --target format-check tidy
ctest --test-dir build/check --output-on-failure
Use cmake --build build/check --target format when you want to rewrite formatting.
The checking targets never rewrite files. Docker supplies the LLVM 22 compiler,
formatter, and analyzer baseline; docker build -t aslice-check . runs the gates.
For ASan and UBSan, add --build-arg ASLICE_SANITIZERS=ON. That matrix uses
libstdc++ because the Ubuntu libc++abi exception-message path triggers an
allocation/deallocation mismatch
under ASan. The ordinary matrix retains libc++ coverage; sanitizer diagnostics
remain enabled. Native Windows builds
run the portable suites and analyze their own compilation database, including
the Windows filesystem implementation. POSIX lifecycle tests run on Linux.
CMake exports compile_commands.json. Set ASLICE_CLANG_FORMAT and
ASLICE_CLANG_TIDY to tool paths at configure time if they are not on PATH.
CLion reads the checked-in configurations; validate its bundled analyzer with
clang-tidy --verify-config. For a standalone analyzer, install its matching
resource headers as well as the executable. tools/quality.py tidy discovers
MinGW's C++ and CRT include paths from the compilation-database compiler.
--resource-dir or ASLICE_CLANG_RESOURCE_DIR can select matching Clang headers.
Do not combine different LLVM versions' intrinsic headers.
Write C++20 with the LLVM layout, four-space indentation, attached braces, and a
100-column limit. Use braces for every control body, with the body on separate
lines. Types use PascalCase; functions and variables use snake_case; private
members end in _. Include the headers that declare what you use, declare one
variable per statement, and name individual imports instead of using a namespace
directive. Document how long borrowed views remain valid.
Validated objects come from factories and keep their invariants private. Pass typed candidates, dependencies, targets, inventories, and generations between subsystems. Parse and serialize JSON at boundaries. The unsigned fixture adapter owns inline payloads and generation JSON; core package and resolver targets must not depend on it. A manifest's document and digest cannot be changed independently.
Fallible operations return core::Result<T>, backed by pinned tl-expected 1.3.1.
Translate library exceptions within the owning boundary. Internal validation
unwinding is caught there; the CLI renders diagnostics and maps failure codes to
exit statuses. Own descriptors, native handles, SQLite statements, and crypto
contexts with move-only RAII, including error paths.
Enabled clang-tidy diagnostics are errors. Generated code and dependencies are excluded. Any suppression must name its check and explain the specific C API or platform constraint beside the affected operation; blanket file suppressions and accepted-warning baselines are not permitted. Formatting, Linux analysis, native Windows builds/tests and analysis, sanitizer tests, demonstrations, and documentation checks are merge gates.
Documentation style
Follow STRUCTURE.md for file placement and the boundary between durable documentation and work records.
The docs ship with the manager, and reviewers hold them to the same bar as formulae. Write the way the code is written: mechanics first, one fact per sentence, one idea per paragraph.
- Say what the thing does, then why. The reader came for the mechanism; motivation follows it.
- Negation is for guarantees, not decoration. "It doesn't X — it Ys" is a tic, not an explanation. Reserve "never"/"not" for places where the reader might genuinely expect the opposite (normative guarantees: "never executes", "not accepted").
- Cut intensifiers. "simply", "exactly", "obviously", "just" do no work; delete them and the sentence keeps its meaning. If it doesn't, the sentence was wrong.
- Mechanisms, not adjectives. Don't call a design clean or robust; state the invariant and let the reader form the adjective.
- Headers describe. "The state database's role", not "The state database's role, stated plainly".
- Tables and code carry the precision; prose carries the reader. A normative must/never/may in prose must also hold in the spec it summarizes.
- History wording is immutable. Never edit historical change descriptions retroactively, even to fix the prose. Preserve links, code, and historical section references; layout and ordering follow the convention below.
Documents with revision records end with one unnumbered ## History section. Put a
Markdown table inside one closed <details> disclosure, with
<summary>Document revision history</summary> and no open attribute. Leave a blank
line before and after the table. Use Version | Date | Changes for versioned
documents and Date | Changes for unversioned documents; do not introduce versions
for an unversioned document.
Keep one row per historical entry. Remove exact duplicates, but retain distinct
accounts of the same revision as separate rows. Sort explicit document versions
numerically, newest first; place a recorded version range by its newest endpoint.
Put entries without a version after versioned entries. Where chronology cannot be
established, preserve source order. Keep the recorded date precision and use
Not recorded for missing metadata rather than inferring it from adjacent entries
or the current status date. Escape table pipes, including pipes in inline code.
Keep current status, version, date, and substantive qualifications at the top; move revision notes from headers and scattered footers into History. Preserve numbered sections and existing link targets. Increment an existing document revision and record each documentation change; for an existing unversioned history, add a dated entry. Software release versions are separate. Documents without revision records need no History section. Archived sources, provenance records, and narrative discussions of history are outside this convention.
Documentation checks
Install the dependencies and run the offline checks from the repository root:
python -m pip install -r tests/requirements.txt
python tests/check_docs.py
python -m unittest discover -s tests -p test_docs.py
python tests/check_contracts.py
python -m unittest discover -s tests -p test_database.py
python -m unittest discover -s tests -p test_recovery_contract.py
python .agents/skills/ref-curator/scripts/refs.py verify --root .
git diff --check
check_docs.py --json emits machine-readable findings. Errors fail the gate;
style warnings remain visible. The checker validates local paths with exact case,
heading fragments, explicit anchors, citation labels, reference-style links,
fences, and table structure. It supports CommonMark, GFM tables, and Pandoc-style
definition lists. External URL availability and factual correctness require
separate review.
History sections and REPOSITORIES' section 9 historical amendment record retain their citations; their links remain checked. Code and inline literals are not prose. Archive originals beginning at the Full captured source heading retain upstream syntax; wrappers remain checked, with bare source-section locators referring to the captured edition. New checker exceptions need a documented reason and a focused regression case.
Before accepting structural changes, inspect rendered glossary, dense-table, long-specification, and man-page examples. Check wrapping, heading destinations, literal placeholders, and definition lists, using Pandoc man output where applicable. Compare histories, requirements, and code examples against the pre-edit files. Archive changes also require byte-for-byte preservation checks of original captures and notices before regenerating checksums.
Conduct
There is no code of conduct document, and there won't be one. People are expected to be decent to each other without a policy forcing it.
Discussion here is direct. This is not a corporate project, and nobody is going to wrap technical feedback in three layers of diplomacy: if you are acting like an idiot, expect to be told that you are acting like an idiot. That is not a conduct violation — it is how direct technical projects talk. Dish it out in good faith, take it in good faith, and keep it about the work.
Maintainer judgment on what's over the line is final. The line is short and obvious: keep it about the work.
License
Original contributions are licensed under Apache-2.0, the project's license. Third-party material retains its applicable license and attribution. By submitting a contribution you agree to Apache-2.0's contribution terms.