Metadata-Version: 2.4
Name: diagit
Version: 0.10.1
Summary: Git fleet diagnostics and AQL-governed control plans with CQRS, event sourcing, protobuf, and URI processes.
Author: Tom Sapletta
Author-email: Tom Sapletta <tom@sapletta.com>
License-Expression: Apache-2.0
Keywords: git,audit,cqrs,event-sourcing,protobuf
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: packaging<27,>=24
Requires-Dist: protobuf<7,>=6.33.5
Requires-Dist: subactor-credential-vault<0.3,>=0.2.1
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: ruff>=0.12; extra == "test"
Requires-Dist: mypy>=1.11; extra == "test"
Requires-Dist: grpcio-tools>=1.66; extra == "test"
Dynamic: license-file

# diagit


## AI Cost Tracking

![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.10.1-blue) ![Python](https://img.shields.io/badge/python-3.11+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.43-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-7.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)

- 🤖 **LLM usage:** $0.4305 (15 commits)
- 👤 **Human dev:** ~$727 (7.3h @ $100/h, 30min dedup)

Generated on 2026-08-10 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)

---

`diagit` is a read-only-by-default auditor for a fleet of Git repositories laid out as
`ROOT/owner/repository`, for example `/home/tom/github/*/*`.

It records immutable protobuf events, builds a disposable SQLite CQRS read
model, and exposes a small allowlisted `diagit://` process URI catalog. It
never fetches, checks out, commits, pushes, or repairs a repository during an
audit. Separate control plans require a digest-bound authority grant and
independent read-back validation.

## Install and run

```sh
python -m pip install -e '.[test]'
diagit audit --root ~/github
diagit audit --root ~/github --organization subactor \
  --repository 'subactor/*-agent' --exclude-repository 'subactor/*-worktree'
diagit audit --root ~/github/semcod --repository todo2code
diagit audit --root ~/github/semcod --layout organization
diagit query summary
diagit query findings --severity SEVERITY_CRITICAL
diagit query repo subactor/core
diagit diagnostics list
diagit diagnostics show DIRTY_WORKTREE
diagit uri 'diagit://fleet/query/summary'
diagit remote audit --organization subactor
diagit remote summary --organization subactor
diagit plan governance --organization subactor --output governance.json
diagit plan repairs --organization subactor --output-dir repair-plans
diagit plan fleet-repairs --organization subactor \
  --repository 'subactor/*-agent' \
  --exclude-repository 'subactor/*-worktree' \
  --code OPENROUTER_APP_IDENTITY_MISSING --output-dir openrouter-repairs
diagit plan governance --organization legacy-org \
  --rename-default-to-main --archive-unmerged --output governance.json
diagit onedev status \
  --onedev-password-file /path/to/onedev-admin-password
diagit forge audit --max-items 1000
diagit forge recommend --require actions --require projects \
  --visibility private --prefer-self-managed
diagit grammar
diagit dsl --expression '```diagitdsl
QUERY SUMMARY
```'
diagit shell
diagit serve --root ~/github --bind 127.0.0.1 --port 8765
```

The default state directory is `$XDG_STATE_HOME/diagit`, or
`~/.local/state/diagit`. Override it with `--state-dir` or
`DIAGIT_STATE_DIR`.

An audit root may be the fleet directory (`ROOT/owner/repository`) or one
organization directory (`ROOT/repository`). Repository selectors containing `/`
match the canonical `owner/repository` ID; selectors without `/` match the bare
repository name. Both forms accept shell-style globs.

`--layout auto` is the default. Use `--layout fleet` or `--layout organization`
when a root contains both direct worktrees and organization directories. A
selected candidate whose Git marker cannot be read remains visible as a typed
`PROBE_FAILED` result instead of disappearing from audit coverage.

## Operator service

Running `diagit` without a subcommand starts the operator service. From
`~/github` it discovers every organization that contains Git worktrees. From
`~/github/<organization>` or any repository below it, it limits the initial
scope to that organization. The initial local audit starts immediately and
reports progress for every completed repository.

In a terminal, the service remains active and accepts single-key shortcuts
without Enter:

```text
a  repeat the local audit
r  audit selected GitHub organizations and compile repair planfiles
d  explicitly approve and delegate the prepared plans to OneDev
s  select one organization or the complete detected scope
q  exit cleanly
```

Delegation requires an additional `y` confirmation. Diagit then creates a
mode-0600, ten-minute authority grant for each exact plan digest and submits
only `CONTROL_ACTION_SUBMIT_REPAIR`; Doctor/Repair/Validator evidence remains
mandatory. When stdin is not a TTY, the same bare invocation performs one
bounded local audit, prints progress, and exits instead of waiting for input.

## Diagnostic registry

Every finding keeps its concise compatibility code, such as
`DIRTY_WORKTREE`, and is enriched from the packaged
`diagit.diagnostic-registry/v2` catalog with a stable identifier such as
`DIAGIT-GIT-003`, category, severity, error class, retryability, and an optional
repair URI. The registry is the source of truth: emitting an unknown code or a
severity/message that differs from its definition fails closed.

The four severities have consistent operational meaning: `INFO` records useful
state, `WARNING` requires planned attention, `ERROR` blocks the requested
invariant, and `CRITICAL` means the audit or control boundary itself is not
trustworthy or available.

Remote pull requests are classified independently of the generic
`OPEN_PULL_REQUEST` finding. The registry distinguishes green mergeable PRs,
failed, pending or missing checks, merge conflicts, unknown mergeability, and
drafts stale for at least 30 days. A green unmerged PR is always emitted as
`PR_GREEN_REQUIRES_MERGE` and cannot be archived by the governance cleanup
policy.

## Architecture

- Commands change only the auditor's own state. `AuditFleet` observes Git
  repositories and appends `AuditRunStarted`, `RepositoryAudited`, and
  `AuditRunCompleted` protobuf events.
- The length-delimited `events.pb` file is the canonical event store. New
  Protobuf events form a deterministic SHA-256 chain and retain read
  compatibility with legacy version 1 history.
- Queries read a rebuildable SQLite projection (`projection.sqlite3`).
- Forge account observations from GitHub, GitLab, Gitea/Forgejo, and Bitbucket
  are stored as `ForgeAccountTwinObserved` protobuf events. Their read model
  ranks providers from visible scopes, repositories, capabilities, visibility,
  completeness, and self-managed preference; it never reads repository content.
  The canonical contract and reviewed decision baseline are pinned to
  [`uri-twin/uri-twin-forge`](https://github.com/uri-twin/uri-twin-forge);
  Diagit returns that source revision with each audit and recommendation.
- `diagit rebuild` recreates the projection solely from stored events.
- URI processes are explicit adapters to the same command/query handlers; an
  arbitrary URI can never become a shell command.

The canonical GBNF request language maps to the same CQRS messages as CLI,
shell, process URI, and loopback REST. Its Wellmanifest contract binds the
grammar, parser, storage schema, examples, command documentation, errors, and
critical security pages by SHA-256. See
[`docs/DSL_ARCHITECTURE.md`](docs/DSL_ARCHITECTURE.md) and
[`dsl-manifest.json`](dsl-manifest.json).

## DSL and API

Every DSL request is exactly one fenced statement:

````text
```diagitdsl
QUERY FINDINGS CODE "*" SEVERITY "SEVERITY_ERROR" LIMIT 1000 OFFSET 0
```
````

`LIMIT` is deliberately bounded to 1000. Fleet controllers must increment
`OFFSET` until the returned page is shorter than the requested limit; values
are never silently truncated.

The REST adapter binds only to loopback and exposes `GET /health`,
`GET /v1/grammar`, CQRS query routes, bounded command routes, and `POST
/v1/dsl`. It does not expose GitHub control-plan application. The interactive
shell accepts the inner statement without Markdown fencing but sends it through
the same GBNF parser.

Command help: [`AUDIT`](docs/AUDIT.md), [`REBUILD`](docs/REBUILD.md),
[`SUMMARY`](docs/SUMMARY.md), [`FINDINGS`](docs/FINDINGS.md), and
[`REPOSITORY`](docs/REPOSITORY.md).

Error help: [`DIAGIT-REQUEST-INVALID`](docs/ERROR/DIAGIT-REQUEST-INVALID.md),
[`DIAGIT-NOT-FOUND`](docs/ERROR/DIAGIT-NOT-FOUND.md),
[`DIAGIT-ROOT-ESCAPE`](docs/CRITICAL/DIAGIT-ROOT-ESCAPE.md), and
[`DIAGIT-EVENT-INTEGRITY`](docs/CRITICAL/DIAGIT-EVENT-INTEGRITY.md).

Supported URI processes:

```text
diagit://fleet/audit?root=/home/tom/github
diagit://fleet/query/summary
diagit://fleet/query/findings?severity=SEVERITY_WARNING&limit=100
diagit://repository/<owner>/<repo>/query/report
diagit://projection/rebuild
diagit://github/<organization>/audit
diagit://github/<organization>/query/summary
diagit://forge/audit?providers=github,gitlab,gitea,bitbucket&max_items=1000
diagit://forge/query/recommend?requires=actions,projects&visibility=private&prefer_self_managed=true
```

## Governed control plans

Remote audit observations can be compiled into the versioned
`diagit.control-plan/v1` JSON DSL. Every executable plan embeds a
`subactor.process-envelope.v2` with all four governed definitions: AQL authority,
transport-independent OQL operations, exact URI processes, and EQL expectations.
A governance plan closes an open pull request or deletes an extra branch only
when the branch head is already contained in `main`. By default, unmerged
branches remain blockers. The explicit `--archive-unmerged` policy first creates
and independently validates a deterministic `diagit/archive/<branch>-<sha>` tag,
then permits branch deletion and PR closure through dependent URI steps.
`--rename-default-to-main` safely migrates a default branch only when `main`
does not already exist and the observed head SHA still matches.

Applying a plan requires a separate mode-0600 `diagit.authority-grant/v1` file.
The grant must name the organization and allowed action kinds, bind the exact
plan SHA-256 digest, identify its approver, and have a future expiry. Execution
is explicit:

```sh
diagit apply governance.json \
  --authority-file authority.json \
  --confirm-apply
```

An archived branch can be restored through a separately authorized plan:

```sh
diagit plan restore --repository owner/repository --branch fix/recover \
  --tag diagit/archive/fix/recover-0123456789ab \
  --expected-head 0123456789abcdef0123456789abcdef01234567 \
  --output restore.json
```

For code repair, generate a single OneDev handoff action. The instruction is
data, never a command:

```sh
diagit plan repair \
  --repository subactor/core \
  --task-id 0004_node-domain-tickets \
  --instruction-file repair-instruction.txt \
  --output repair.json
```

Before applying an action, Diagit fails closed unless its AQL actor explicitly
allows the matching OQL operation and the action is bound to exactly one URI
step. After an authorized apply, Diagit sends the complete envelope, canonical
plan digest, grant ID, and approving principal to the existing OneDev Agent
`/tasks/execute` boundary. OneDev independently repeats the AQL/OQL/URI/EQL
checks before materializing a Planfile ticket. OneDev then owns Doctor → Repair
→ Validator, test gates, exact-SHA checks, and publication. A submission receipt
remains `submitted`; only validator evidence can satisfy the terminal EQL.
The referenced `diagit-repair-delegation.pl.aql` decision model is packaged with
the wheel, so the envelope never relies on an unresolved model name.

`diagit plan repairs` compiles the latest remote audit into one independently
authorizable plan per affected repository plus a
`diagit.repair-plan-set/v1` manifest. It selects only diagnostics whose
registered repair URI delegates to OneDev, deduplicates them into a bounded
instruction, and preserves the source audit run ID. `--code` can be repeated
to restrict generation to specific delegable diagnostic codes. Generating the
set never submits work; each plan still requires its own digest-bound authority
grant before `diagit apply` can cross the OneDev boundary.

`diagit audit` accepts repeatable exact or glob repository selectors. The
`fleet-repairs` variant compiles local findings from the latest completed audit,
so one diagnostic becomes an independently authorized OneDev ticket for each
selected repository rather than one unbounded fleet mutation.

## OpenRouter application attribution

`DIAGIT-LLM-001 / OPENROUTER_APP_IDENTITY_MISSING` identifies tracked runtime
consumers of OpenRouter that do not provide verifiable cost attribution. A
compliant repair declares `OPENROUTER_APP_NAME` and `OPENROUTER_APP_URL` in the
environment template and binds them to `X-OpenRouter-Title` and `HTTP-Referer`.
When the name is blank, runtime code must use the repository directory name.
Environment templates and policy implementations are not consumers by
themselves. Applications routed through `subllm` receive provider identity from
its central application/function policy and do not duplicate this local binding.
Diagit reads only variable names and bounded tracked source files; finding
evidence never contains environment values or API keys.

The audit uses only local Git metadata. Ahead/behind values therefore describe
the locally known upstream refs and do not imply a network refresh.

## Release readiness

`diagit audit --root ~/github --organization subactor --check-registries` adds bounded
release checks to the normal event-sourced fleet audit. Local checks validate `goal.yaml`,
package file boundaries, and version tags. Registry checks compare public `package.json`
and `pyproject.toml` manifests with npm and PyPI; Node packages with `private: true` are
never treated as publication candidates.

The command is read-only: it reports unpublished packages and version drift but never
publishes, tags, or pushes. Registry access is opt-in because it requires network I/O.

## Development

```sh
python -m grpc_tools.protoc -Iproto --python_out=src --pyi_out=src proto/diagit/v1/audit.proto
scripts/verify
scripts/verify-dsl
```

The package distribution name is `diagit`, its Python import is
`diagit`, and its executable is `diagit` (which also permits the Git
external-command form `git diagit`).


## License

Licensed under Apache-2.0.
