aayudha docs Built from de79988 (2026-10-26)

Contributing

Thanks for helping. Scope and sequencing live in docs/BUILD-PLAN.md; read the work package (WP) your change belongs to before you start. Report security issues privately as described in SECURITY.md, not here.

Features marked "n/a until WPx.y" do not exist yet (ground rule 1a).

Requirements

Branches

Branch Role
dev Default branch and nightly integration. Every feature, bugfix, and hotfix PR targets it.
rolling Canary snapshots, promoted from dev.
main Stable production. Every commit is tagged v* and released.
ledger Orphan branch for this repository's own proof packs. Not for code.

Never open a PR against rolling or main. Promotions are done by the maintainer (below).

Pull request flow

  1. Work in a dedicated worktree and branch cut from the latest dev:

    git fetch origin && git worktree add ../wt-<branch> -b <branch> origin/dev
    
  2. Make the change. Run go build ./... && go vet ./... && go test ./... and go generate ./... (see Generated files), plus golangci-lint run ./... and markdownlint-cli2 if you have them.

  3. Commit with Conventional Commits and a DCO sign-off (below).

  4. Open a PR to dev. CI must be green: the required checks are build, generate-drift, lint, commits, security, docs, release-snapshot (.github/workflows/ci.yml) and protected-paths. Jobs that a PR's paths cannot affect report "skipped", which counts as passing. build also runs the coverage ratchet: ratchets.toml says what it checks and how to tighten the floor.

  5. The PR is squash-merged into dev once every required check is green and inspect's disposition at the head is merge, never with a red required check or an open fix finding. A Story PR (its body says Refs #n for a Story) merges only through aayu ship <pr>, which merges it on proof and lands its proof. A Story PR in the narrow set ([autonomy] always_person) waits for a person: ship comments Ready to ship, and a person ships it from aayu inbox. If ship refuses for anything else, it is not merged. An agent's PR carries the agent-authored label and an Aayudha-Host: line (and an Aayudha-Model: line per known model) in its body and in each commit's trailers, so the autonomy streak counts it. Then delete the branch and the worktree:

    git worktree remove ../wt-<branch> && git branch -D <branch>
    

When the build plan cannot be enforced as written, build the closest enforceable alternative and record it in the PR description under a "Deviation from the plan:" heading: what the plan assumed, what you found, and what you built instead. The squash commit message keeps it, so git log is the record. Real decisions are aayu Decision issues. Appendix B of the plan, the decision log up to 2026-10-17, is archived privately; IDs such as D-5.5c.24 in code and docs refer to it, and no new rows are added. Research notes live in the private repository rahulnakmol/agentic-ayudha-research; cite one as "private research note <file>" with its section, never as a link or a path.

From PR11 onward every work package is tracked as a Story issue on this repository with a Completion Goal (ground rule 1); before that, work is tracked in the build plan.

Promotion (agent-run, no human approval)

Founder policy for this repository: promotions dev → rolling and rolling → main need no human approval. Whoever promotes (the maintainer or an agent acting with the maintainer's credentials) does so as soon as the gate is green; nothing waits for a person in between. The gates themselves and the commit and tag conventions are unchanged. dev → rolling happens on the release schedule or an explicit release request; rolling → main on an explicit stable-release request.

Only the repository admin identity can move rolling and main or create v* tags (rulesets rolling-main-promotion and release-tags). The gate runs on the exact merge commit that will land, built on a throwaway promote/… branch, so what was tested is what ships.

The explicit stable-release request is a comment on the Release issue whose first line is Stable release: vX.Y.Z, written after the Release's rolling snapshot by a person (not a bot) with write, maintain, or admin access. An agent never writes it; aayu release --to main refuses without it.

aayu release <release-issue> --to rolling|main (WP7.8), which an agent may run, executes exactly the steps below for a Release issue. It also re-runs failed gate jobs once, stamps the stories the promotion shipped released, publishes the release notes, records the shipping evidence on the ledger and in the Release, and makes the Goals above the stories due for measure. --dry-run shows what would ship. The commands below are the same procedure by hand.

dev → rolling

The source is the newest dev commit that already has tier-2 results for every gating group: an eval-results-<sha>-<group> artifact on a completed evals.yml run of that commit, for each group go run ./internal/evals/cmd/eval -groups -gating prints. That is what the gate's evals-gate downloads. Pushes to dev and the nightly run share one concurrency group, so in a busy merge window the intermediate commits get no run and only the newest does. If dev's newest commit has no results yet, either promote the newest older commit that has them, or dispatch once on the newest (gh workflow run evals.yml --ref dev), wait for it, and check again. Never dispatch again to get a better result. aayu release <n> --to rolling does this check, never dispatches, and names both choices; --at <sha> promotes the older commit.

git fetch origin
src="$(git rev-parse origin/dev)"                 # or the newest older dev commit with results
for id in $(gh run list --workflow evals.yml --commit "$src" --status completed --json databaseId --jq '.[].databaseId'); do
  gh api "repos/{owner}/{repo}/actions/runs/$id/artifacts" --jq '.artifacts[].name'
done                                              # every gating group's eval-results-$src-<group> must be listed
ts="$(date -u +%Y%m%dT%H%MZ)"; cand="promote/rolling-$ts"; wt="../wt-promote-rolling-$ts"
git worktree add "$wt" -b "$cand" origin/rolling && cd "$wt"
git merge --no-ff --signoff "$src" -m "Rolling snapshot: $ts"
git push origin "$cand"
gh workflow run ci.yml --ref "$cand" -f gate=rolling && sleep 15
gh run watch "$(gh run list --workflow ci.yml --branch "$cand" --event workflow_dispatch --limit 1 --json databaseId --jq '.[0].databaseId')" --exit-status
git push origin HEAD:rolling                      # fast-forward to the gated commit
git tag "rolling-$(date -u +%Y-%m-%d)" && git push origin "rolling-$(date -u +%Y-%m-%d)"
git push origin --delete "$cand" && cd - && git worktree remove "$wt" && git branch -D "$cand"

The rolling gate (gate=rolling) is the full PR suite (build matrix and size budget, unit tests, lint, generate drift, security scans, docs, and release-snapshot: a signed goreleaser snapshot that install.sh must accept while refusing tampered copies) plus integration (tests under the integration build tag, race detector), benchmarks, and evals-gate: the tier-2 skill eval results of the commit being promoted (from evals.yml, which runs on every push to rolling and main and nightly on dev) must pass the WP3.4 bar and not regress against main's results. A second promotion on the same day uses a rolling-<date>.<n> tag.

rolling → main

git fetch origin
v=vX.Y.Z; cand="promote/main-$v"; wt="../wt-promote-main-$v"
git worktree add "$wt" -b "$cand" origin/main && cd "$wt"
git merge --no-ff --signoff origin/rolling -m "Release: $v"
git push origin "$cand"
gh workflow run ci.yml --ref "$cand" -f gate=stable && sleep 15
gh run watch "$(gh run list --workflow ci.yml --branch "$cand" --event workflow_dispatch --limit 1 --json databaseId --jq '.[0].databaseId')" --exit-status
git push origin HEAD:main
git tag -a "$v" -m "Release $v: <summary>" && git push origin "$v" && sleep 15   # starts release.yml
gh run watch "$(gh run list --workflow release.yml --branch "$v" --limit 1 --json databaseId --jq '.[0].databaseId')" --exit-status
git push origin --delete "$cand" && cd - && git worktree remove "$wt" && git branch -D "$cand"

The stable gate (gate=stable) is everything in the rolling gate plus extended-integration (live tests behind AAYU_LIVE=1; the first ones land in WP1.4/WP2.3) and smoke (unit tests and a binary smoke test on Linux, macOS, and Windows). Security scans run in security on every gate.

Every commit on main is tagged and released. The annotated v* tag starts .github/workflows/release.yml, which needs no human step: it refuses a tag whose commit is not on main or that is not annotated, builds with goreleaser, signs checksums.txt with cosign (keyless), attests SLSA provenance, attaches SBOMs, checks that install.sh accepts the release, installs it from GitHub on Linux and macOS, and publishes the brew cask, Scoop manifest, and winget PR. Resume a transient failure with gh run rerun <run-id> --failed. If anything fails, do not promote: fix it through dev, publish a new rolling snapshot, then retry. The current release is supported, plus security patches for the previous two minor versions; record each minor version's end-of-life date in its release notes.

Release publishing setup (founder)

release.yml publishes to package managers from separate jobs, each in its own GitHub environment so its token never reaches the build job. Until these exist, those three jobs fail with a message naming what is missing; the GitHub release itself is unaffected.

Environment Secret Needs
release-homebrew HOMEBREW_TAP_TOKEN Repository rahulnakmol/homebrew-tap; a fine-grained token with contents read/write on it only
release-scoop SCOOP_BUCKET_TOKEN Repository rahulnakmol/scoop-bucket; a fine-grained token with contents read/write on it only
release-winget WINGET_TOKEN A fork rahulnakmol/winget-pkgs of microsoft/winget-pkgs; a classic token with public_repo (it opens a PR on the upstream repository)

Emergency path

For a critical security incident or outage only:

  1. Branch from rolling or main (wherever the fix must land).
  2. Open the fix as a PR to dev and merge it there first.
  3. Cherry-pick that exact commit to rolling and/or main.
  4. Run the release gate, tag, and release.
  5. Record why the emergency path was used in the PR and in the release notes (and in the security advisory if there is one).

Commits

The DCO is required from the first commit so that every contribution's origin is recorded; it keeps a future relicense possible (plan risk R10). There is no separate CLA.

Developer Certificate of Origin

By adding Signed-off-by you certify the following (from https://developercertificate.org?utm_campaign=docs-site&utm_medium=docs&utm_source=agentic-ayudha):

Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.

Generated files

internal/verbs/registry.go is the single source for verbs and plumbing commands (ground rule 4). These are generated from it by go generate ./...:

CONSTRAINTS.md is generated the same way from internal/config/defaults.toml, the default configuration embedded in the binary (WP0.5). The generator in internal/config/gen holds each key's meaning and plan source and fails if a key is added or removed without updating it.

Do not edit generated output by hand. Change the registry, run go generate ./..., and commit both. The CI job generate-drift fails on any difference.

Protected paths

These paths need a human CODEOWNER review (see .github/CODEOWNERS):

.github/, skills/, ops/, seats/, CONSTRAINTS.md, internal/config/defaults.toml, .aayudha/config.toml, .agents/, .claude/; the plugin bundle (.claude-plugin/, .codex-plugin/, .cursor-plugin/, the hosts/*/hooks.json files, hosts/amp/aayudha.ts); and every host's executable config in the checkout (.mcp.json, .cursor/, .codex/, .amp/plugins/, .github/hooks/), the aayudha:host-config block that aayu init --gates writes.

Identities and approvals

Solo-mode policy

This policy changes as the project gains people. Change it by PR, together with the matching ruleset and .github/CODEOWNERS changes.

Today: there is one human, rahulnakmol. GitHub never lets a PR author approve their own PR, and native CODEOWNER review does not block a PR authored by the only code owner, so the CODEOWNER rule is enforced by a gate instead (docs/BUILD-PLAN.md Appendix B, D2).

Rule while there is one human:

  1. The protected-paths check (.github/workflows/protected-paths.yml) fails on any PR that changes a path in .github/CODEOWNERS. It runs from the default branch's copy of the workflow, so a PR cannot weaken it.

  2. The protected-paths-codeowner-review ruleset requires that check on dev. Its only bypass is the repository admin, and only through a pull request, never by direct push. So a protected-path PR merges only when the founder deliberately merges it with "bypass rules" (gh pr merge --admin). Agents never use the bypass on their own initiative.

  3. GitHub records each bypass in the ruleset rule-suite history:

    gh api "repos/rahulnakmol/agentic-ayudha/rulesets/rule-suites?rule_suite_result=bypass&time_period=month"
    
  4. aayu audit (WP7.6; hand-run reconciliations until then) lists every such bypass as a solo-mode bypass when a PR comment starting Solo-mode bypass: came before it and only the protected-paths check failed, and writes its report to ledger under audit/<date>/. It runs with a repository admin token, since only an admin can read rule suites. A bypass the report marks unexplained is an incident (see SECURITY.md).

When a bot identity exists (WP1.4): agent-authored PRs are opened by the bot, so the founder can approve them as a real review. The protected-paths gate passes when every protected path in the PR has an approving review of the head commit from one of its CODEOWNERs who is a human, not a bot login, and not the author (internal/identity, run from the base branch). Submitting or dismissing a review re-runs the check (protected-paths-review.yml → protected-paths-rerun.yml). Bypasses then remain only for PRs the founder authors.

When a second human seat is added (aayu seat add <seat> --human <login>, WP5.1): the rule switches to true second-human review: the protected-paths-codeowner-review ruleset requires one approval with native CODEOWNER review, and its protected-paths check and admin bypass are removed. It takes two runs, because GitHub reads CODEOWNERS from the base branch and never lets an author approve their own PR:

  1. The first run writes [seats.<seat>], the aayudha:seats block at the end of .github/CODEOWNERS (the seat's layer paths, and with two humans every protected path names both, so each can review the other), and the RACI section of AGENTS.md, and opens the onboarding issue. The founder merges these files with a solo-mode bypass.
  2. Running the same command again once that change is on dev switches the ruleset.

aayu seat remove <seat> reverses it in one run: it returns the ruleset to solo mode first (gate check required, admin bypass back), so the founder can merge the CODEOWNERS change as before. Seat changes are recorded on ledger in seats/changes.jsonl.

Third-party code

Every new dependency must have its licence recorded in THIRD_PARTY_NOTICES.md in the same PR. CI licence scanning (go-licenses and an npm scan in the security job) gates it. This project is BSD-3-Clause (LICENSE); by contributing you agree your contribution is licensed under it.