Contents

Warnings and sync

A warning tells an agent that its work and another agent's work affect each other, so the agent can adapt while that is still cheap. Sync brings other work into an agent's copy when you ask for it.

When a warning arrives

An agent gets the warning at its next step, and the check takes about 0.2 seconds. This is also true when a person or a tool without gitta edits the other workspace. A new file that nobody has added yet shows up in gitta status at once, and at an agent's step within five minutes.

Kinds of warning

WarningMeaningWhat to do
Changed by another agentAnother agent changed a function, method, constant or field that your new code calls. gitta shows the other agent's new definition line. This is information for the agent. It never stops a merge whose tests pass. When the changed function and the code that uses it are both the same agent's work, gitta says nothing. An agent gets one such warning for each other agent and file, naming up to three functions, and no repeat when more functions in that file change.Check that the code still fits. The tests at merge time decide.
Also editingTwo agents changed the same function or the same lines.Nothing. gitta writes it to .gitta/gitta.log and gitta watch, merges both changes by structure, and the tests at merge time decide. The agent is not told.
Changed on mainWork already on main changed something your new code uses.gitta notes it in .gitta/gitta.log and checks it at merge time. If the tests then fail, the merge output shows the failing test, its first error line and the new definition on main.
ClaimedAnother agent announced that it is about to change a symbol you use.Wait for it to be merged, or coordinate.
SecurityAnother agent removed or switched off a security control, such as a login check, a permission check, a CSRF check, a rate limit, input validation or a certificate check, and your new code calls the code that it protected. Severity is always high.Do not rely on the protection. Add the check to your own code. The merge output and the log report it.
Security noteYour own new code has a known risk, such as SQL built from text and variables or certificate checks turned off. Severity is medium or high.Use the safer pattern that the note names. A high-severity note counts as a weakened check at merge time.
Weakened checksYou deleted or skipped tests, removed assertions or relaxed CI.Fix the code instead. The merge reports it, and stops when weakened_checks is block.
CredentialA file contains what looks like a secret.Remove it and read it from configuration.
Agent instructionsYou changed AGENTS.md, CLAUDE.md or a similar file.Change them only when a person asked for it.

Security controls

gitta knows the common security controls of each language. When one agent removes or switches off a control, and another agent's new code calls the code that the control protected, both agents get a security warning at their next step. The output below comes from microblog, the Flask app of Miguel Grinberg's Flask Mega-Tutorial. One agent makes profile pages public and removes @login_required from the user view. Another agent adds a short /u/<username> link whose view calls user, and relies on its login check. The agent whose code calls it sees this:

 In the short-links agent's session
Gitta: SECURITY (high severity). Agent public-profiles, working in parallel, removed the login check (`@login_required`) from `app.main.routes.user`, which your new code in app/main/routes.py calls. Your code may now run without a logged-in user. Do not rely on that protection: add the check to your own code, or tell the user. Tell the user in one sentence. gitta reports this when the work is merged, and the team policy in .gitta.toml decides whether it stops the merge.

The agent that removed the control sees this:

 In the public-profiles agent's session
Gitta: SECURITY (high severity). You removed the login check (`@login_required`) from `app.main.routes.user`, and agent short-links has new code in app/main/routes.py that calls it. Their code may now run without a logged-in user. Restore the check unless the user asked for this, and tell the user.

Gitta: SECURITY. You weakened a security control in app/main/routes.py: removed the login check (`@login_required`) from `app.main.routes.user`. Code that calls it may now run without a logged-in user. Do not remove or switch off security checks unless the user asked for it. If it must change, explain why to the user in the chat. gitta reports this when the work is merged, and the team policy in .gitta.toml decides whether it stops the merge.

gitta status shows both:

 Terminal
$ gitta status
main is the branch gitta merges finished work into.
  public-profiles 1 file changed, removes tests or weakens a security check; reported when merged, and .gitta.toml decides whether that stops the merge
       removed the login check (`@login_required`) from `app.main.routes.user`. Code that calls it may now run without a logged-in user.
  short-links 1 file changed, ready to merge
  SECURITY potentially affected: public-profiles removed the login check (`@login_required`) from `app.main.routes.user`, which short-links' new code in app/main/routes.py calls. That code may now run without a logged-in user.
  SECURITY: app/main/routes.py by public-profiles: removed the login check (`@login_required`) from `app.main.routes.user`. Code that calls it may now run without a logged-in user. Reported when merged; .gitta.toml decides whether it stops the merge.

What gitta treats as a security control

gitta knows the common security controls of Python, TypeScript and JavaScript, Java and Kotlin, C#, Go, Rust, C and C++, and COBOL with gitta Enterprise. They are login and permission checks, CSRF protection, rate limits, input validation, HTML escaping and certificate checks, written as decorators, attributes, annotations, guards or middleware. Functions whose names say that they check, authorise, verify, validate, sanitise or escape count too. JCL, REXX, COBOL and assembler also have their own rules for one agent’s changes, see Security checks on mainframe code.

What counts as weakened

Other changes to a check, for example new parameters, stay normal warnings about a changed function. gitta adds that the symbol is a security check. A stricter check, a new check or an unrelated edit in an auth module gives no security warning.

Mark your own security checks

Put the symbols and paths that gitta must treat as security checks in a file .gitta-security at the top of the repository, one on each line. Commit it, so the whole team and CI use it.

 Terminal
# .gitta-security
app.main.routes.edit_profile
app.auth.*
app/api/auth.py

A line with / or a file name is a path. Other lines are symbols, as gitta names them in warnings. * matches any text. You can also add patterns to one checkout only with gitta init --security PATTERN. An agent that removes a line from .gitta-security is reported as a weakened check.

Risky code in an agent's own change

gitta also checks the lines that an agent adds for well-known security risks. It looks only at the agent's own changed files, and only at lines that are new. Code that was already there is never reported, and a line that only moved is not new. The check takes a few milliseconds, so the agent hears about a risk at the next step, while it still has the code in front of it.

RiskSeverity
SQL built from text and variablesMedium
A shell command built from text and variablesHigh
Code run from a string built at run timeMedium
Objects loaded with a loader that can run code from the dataHigh
Certificate checks turned offHigh
Any website allowed to send requests with the user's cookiesMedium
CSRF protection turned offMedium
Passwords hashed with a fast hashMedium
Secrets in the code, beyond the credentials that are always refusedMedium
Secrets written to a log or the consoleMedium
C and C++: a copy into a buffer with no size limit, or memcpy with a length nobody checkedMedium
C and C++: a variable used as the format of printf and similar callsMedium

The rules cover Python, TypeScript and JavaScript, Go, Java, Kotlin, C#, Rust, C and C++. In C and C++, shell commands include system, popen and the exec calls, and certificate checks include OpenSSL, mbed TLS and curl settings. Mainframe code has its own rules in gitta Enterprise. In test code every note is medium. The agent sees each note once, in its session. In this microblog run, one agent adds a flask translate check command that runs pybabel through os.system with the language code in the string. Another adds a user search that puts the query text into SQL:

 In the agents' sessions
gitta: security note (high): app/cli.py:47 runs a shell command built from text and variables. Pass the program and its arguments as a list, without a shell. gitta reports this when the work is merged, and the team policy in .gitta.toml decides whether it stops the merge.
gitta: security note (medium): app/main/routes.py:93 builds SQL from text and variables. Use a parameterised query.

gitta status shows the open notes with the rule and the agent:

 Terminal
$ gitta status
main is the branch gitta merges finished work into.
  translate-stats 1 file changed, removes tests or weakens a security check; reported when merged, and .gitta.toml decides whether that stops the merge
       line 47 runs a shell command built from text and variables. Pass the program and its arguments as a list, without a shell. [shell-text].
  user-search 1 file changed, ready to merge
  security note (high): app/cli.py:47 runs a shell command built from text and variables. Pass the program and its arguments as a list, without a shell. [shell-text, by translate-stats]
  security note (medium): app/main/routes.py:93 builds SQL from text and variables. Use a parameterised query. [sql-text, by user-search]

gitta status --json lists them under security_notes, and gitta pr-check adds a Security line for each one. Medium notes are shown at merge time and recorded. High notes count as weakened checks, see Merging. You choose the level with --security-block.

Allow a reviewed line

When a person has checked a line and it is fine, mark it with a comment on the same line. Give the rule and a reason:

 app/main/routes.py
row = db.session.execute(sa.text(f"SELECT username FROM user WHERE id = {int(id)}")).first()  # gitta: allow sql-text id is cast to int

gitta then reports the allow instead of the note:

 Terminal
$ gitta land user-count
gitta: allowed sql-text at app/main/routes.py:92 (id is cast to int)
gitta: landed user-count as 86711c470c

To allow a rule for whole paths, add a section to the committed .gitta-security file. Each line names the rule, a path pattern and the reason:

 Terminal
# .gitta-security
[allow]
shell-text app/cli.py the language code comes from the developer running the command

Every allow that is used is recorded in the audit trail as security_allowed, with the reason. An allow that the agent adds in its own change silences the note, and gitta still reports that the agent added it.

Where warnings appear

An agent sees each warning once. gitta does not repeat it at every step, and the repair note after a failed merge comes once per attempt.

Sync

Sync from main brings in work that was merged since the agent started:

 Terminal
$ gitta sync ci99
gitta: merged 2 file(s) from main into ci99: bench/aidev-replay/analyze.py, bench/aidev-replay/test_wilson.py
gitta: note: ci-level was merged into main and changed `wilson`, which ci99's new code in bench/aidev-replay/analyze.py calls. ci99 now has the new version. Check that bench/aidev-replay/analyze.py still works with it. The tests on the combined code decide when it is merged.

Sync is all or nothing. If one file cannot be merged, no file changes, and gitta names the file. The agent's own changes are always kept.

Automatic sync

gitta syncs the workspaces it made itself, with gitta run or gitta up, from main at each agent step, when the work merges cleanly and nothing their new code uses changed meaning. A worktree made by Claude Code, Cursor, Codex or git worktree add keeps exactly what its agent wrote. git brings main in when the agent merges, and the tests at merge time decide. To turn automatic sync off, set "auto_sync": false in .gitta/config.json.

Test a combination

gitta check runs your gate on an agent's work combined with each other agent's work. It does not change any worktree. You run it when you want to. gitta never runs it for agents, because the merge runs the tests.

 Terminal
$ gitta check ci99
gitta: ci99 + ci-level: BREAKS
      File "/Users/you/src/gitta/.gitta/wt/check-0/bench/aidev-replay/test_ci99.py", line 7, in test_wider
        lo, hi = ci99(50, 100)
                 ~~~~^^^^^^^^^
      File "/Users/you/src/gitta/.gitta/wt/check-0/bench/aidev-replay/analyze.py", line 35, in ci99
        p, lo, hi = wilson(k, n, 2.576)
                    ~~~~~~^^^^^^^^^^^^^
    TypeError: wilson() takes 2 positional arguments but 3 were given
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.001s
    
    FAILED (errors=1)

Add --with AGENT to test against one agent only, and --json for scripts. Results are PASSES, BREAKS, DOES_NOT_MERGE or CANNOT_RUN. CANNOT_RUN means a tool that the gate needs was not found, so the tests did not run. That is a setup problem for you to fix, and agents are not told that their work breaks. Checks run in parallel. Use gitta init --parallel N to limit how many run at once.

Claims

Before a large change, an agent can claim the symbol or path. Others are warned before they build on it.

 Terminal
$ gitta claim ci-level bench.aidev-replay.analyze.wilson "making z keyword-only"
gitta: ci-level claimed bench.aidev-replay.analyze.wilson
$ gitta release ci-level bench.aidev-replay.analyze.wilson
gitta: ci-level released bench.aidev-replay.analyze.wilson