aglium-ledger 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aglium_ledger-0.1.0/.claude-plugin/marketplace.json +17 -0
- aglium_ledger-0.1.0/.github/workflows/ci.yml +46 -0
- aglium_ledger-0.1.0/.github/workflows/release.yml +44 -0
- aglium_ledger-0.1.0/.gitignore +9 -0
- aglium_ledger-0.1.0/LICENSE +96 -0
- aglium_ledger-0.1.0/PKG-INFO +161 -0
- aglium_ledger-0.1.0/README.md +143 -0
- aglium_ledger-0.1.0/action.yml +63 -0
- aglium_ledger-0.1.0/config/ledger.example.yml +151 -0
- aglium_ledger-0.1.0/docs/client-vs-server.md +81 -0
- aglium_ledger-0.1.0/docs/install.md +73 -0
- aglium_ledger-0.1.0/docs/replay.md +80 -0
- aglium_ledger-0.1.0/docs/trust.md +59 -0
- aglium_ledger-0.1.0/plugin/.claude-plugin/plugin.json +17 -0
- aglium_ledger-0.1.0/plugin/.mcp.json +8 -0
- aglium_ledger-0.1.0/plugin/LICENSE +96 -0
- aglium_ledger-0.1.0/plugin/README.md +97 -0
- aglium_ledger-0.1.0/plugin/commands/brief.md +25 -0
- aglium_ledger-0.1.0/plugin/commands/coverage.md +13 -0
- aglium_ledger-0.1.0/plugin/commands/replay.md +41 -0
- aglium_ledger-0.1.0/plugin/commands/setup.md +34 -0
- aglium_ledger-0.1.0/plugin/hooks/hooks.json +17 -0
- aglium_ledger-0.1.0/plugin/scripts/ledger-run.sh +69 -0
- aglium_ledger-0.1.0/plugin/skills/ledger/SKILL.md +109 -0
- aglium_ledger-0.1.0/pyproject.toml +35 -0
- aglium_ledger-0.1.0/src/ledger/__init__.py +0 -0
- aglium_ledger-0.1.0/src/ledger/adapters/__init__.py +0 -0
- aglium_ledger-0.1.0/src/ledger/adapters/vcs/__init__.py +0 -0
- aglium_ledger-0.1.0/src/ledger/adapters/vcs/base.py +277 -0
- aglium_ledger-0.1.0/src/ledger/adapters/vcs/bitbucket.py +126 -0
- aglium_ledger-0.1.0/src/ledger/adapters/vcs/fake.py +120 -0
- aglium_ledger-0.1.0/src/ledger/adapters/vcs/github.py +194 -0
- aglium_ledger-0.1.0/src/ledger/adapters/vcs/localgit.py +704 -0
- aglium_ledger-0.1.0/src/ledger/bench.py +252 -0
- aglium_ledger-0.1.0/src/ledger/cli.py +1084 -0
- aglium_ledger-0.1.0/src/ledger/config.py +202 -0
- aglium_ledger-0.1.0/src/ledger/core/__init__.py +0 -0
- aglium_ledger-0.1.0/src/ledger/core/bases.py +32 -0
- aglium_ledger-0.1.0/src/ledger/core/bindings.py +160 -0
- aglium_ledger-0.1.0/src/ledger/core/breaks.py +266 -0
- aglium_ledger-0.1.0/src/ledger/core/brief.py +848 -0
- aglium_ledger-0.1.0/src/ledger/core/card.py +111 -0
- aglium_ledger-0.1.0/src/ledger/core/check.py +94 -0
- aglium_ledger-0.1.0/src/ledger/core/contracts.py +472 -0
- aglium_ledger-0.1.0/src/ledger/core/credentials.py +80 -0
- aglium_ledger-0.1.0/src/ledger/core/decision.py +263 -0
- aglium_ledger-0.1.0/src/ledger/core/declarations.py +252 -0
- aglium_ledger-0.1.0/src/ledger/core/docs.py +134 -0
- aglium_ledger-0.1.0/src/ledger/core/effect.py +442 -0
- aglium_ledger-0.1.0/src/ledger/core/estate.py +219 -0
- aglium_ledger-0.1.0/src/ledger/core/evidence.py +156 -0
- aglium_ledger-0.1.0/src/ledger/core/gate.py +30 -0
- aglium_ledger-0.1.0/src/ledger/core/grounding.py +97 -0
- aglium_ledger-0.1.0/src/ledger/core/join.py +128 -0
- aglium_ledger-0.1.0/src/ledger/core/naming.py +74 -0
- aglium_ledger-0.1.0/src/ledger/core/overlap.py +293 -0
- aglium_ledger-0.1.0/src/ledger/core/rank.py +272 -0
- aglium_ledger-0.1.0/src/ledger/core/redact.py +130 -0
- aglium_ledger-0.1.0/src/ledger/core/render.py +146 -0
- aglium_ledger-0.1.0/src/ledger/core/tickets.py +63 -0
- aglium_ledger-0.1.0/src/ledger/coverage.py +204 -0
- aglium_ledger-0.1.0/src/ledger/feedback.py +213 -0
- aglium_ledger-0.1.0/src/ledger/init.py +174 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/__init__.py +0 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/collect.py +226 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/decisions.py +80 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/mirrors.py +183 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/overlap_scan.py +135 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/scan.py +206 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/signals.py +80 -0
- aglium_ledger-0.1.0/src/ledger/pipeline/wiring.py +180 -0
- aglium_ledger-0.1.0/src/ledger/remote.py +47 -0
- aglium_ledger-0.1.0/src/ledger/replay.py +791 -0
- aglium_ledger-0.1.0/src/ledger/service.py +1326 -0
- aglium_ledger-0.1.0/src/ledger/surfaces/__init__.py +0 -0
- aglium_ledger-0.1.0/src/ledger/surfaces/hook.py +800 -0
- aglium_ledger-0.1.0/src/ledger/surfaces/mcp.py +250 -0
- aglium_ledger-0.1.0/src/ledger/upgrade.py +106 -0
- aglium_ledger-0.1.0/tests/adapters/test_vcs_contract.py +532 -0
- aglium_ledger-0.1.0/tests/conftest.py +9 -0
- aglium_ledger-0.1.0/tests/lab_estate.py +312 -0
- aglium_ledger-0.1.0/tests/test_bases.py +26 -0
- aglium_ledger-0.1.0/tests/test_bench.py +58 -0
- aglium_ledger-0.1.0/tests/test_beyond_tickets.py +160 -0
- aglium_ledger-0.1.0/tests/test_bindings.py +77 -0
- aglium_ledger-0.1.0/tests/test_boundary.py +46 -0
- aglium_ledger-0.1.0/tests/test_breaks.py +293 -0
- aglium_ledger-0.1.0/tests/test_brief.py +527 -0
- aglium_ledger-0.1.0/tests/test_check.py +67 -0
- aglium_ledger-0.1.0/tests/test_cli.py +207 -0
- aglium_ledger-0.1.0/tests/test_cli_render.py +33 -0
- aglium_ledger-0.1.0/tests/test_client_surfaces.py +154 -0
- aglium_ledger-0.1.0/tests/test_collect.py +213 -0
- aglium_ledger-0.1.0/tests/test_contracts.py +227 -0
- aglium_ledger-0.1.0/tests/test_coverage_feedback.py +134 -0
- aglium_ledger-0.1.0/tests/test_credentials.py +141 -0
- aglium_ledger-0.1.0/tests/test_decision.py +116 -0
- aglium_ledger-0.1.0/tests/test_declarations.py +403 -0
- aglium_ledger-0.1.0/tests/test_effect.py +208 -0
- aglium_ledger-0.1.0/tests/test_estate.py +178 -0
- aglium_ledger-0.1.0/tests/test_gate.py +94 -0
- aglium_ledger-0.1.0/tests/test_grounding.py +80 -0
- aglium_ledger-0.1.0/tests/test_hook.py +728 -0
- aglium_ledger-0.1.0/tests/test_init.py +131 -0
- aglium_ledger-0.1.0/tests/test_join.py +94 -0
- aglium_ledger-0.1.0/tests/test_mirrors.py +291 -0
- aglium_ledger-0.1.0/tests/test_naming.py +88 -0
- aglium_ledger-0.1.0/tests/test_overlap.py +189 -0
- aglium_ledger-0.1.0/tests/test_plugin.py +120 -0
- aglium_ledger-0.1.0/tests/test_rank.py +74 -0
- aglium_ledger-0.1.0/tests/test_redact.py +89 -0
- aglium_ledger-0.1.0/tests/test_render.py +82 -0
- aglium_ledger-0.1.0/tests/test_replay.py +371 -0
- aglium_ledger-0.1.0/tests/test_scan_cache.py +126 -0
- aglium_ledger-0.1.0/tests/test_secrets.py +28 -0
- aglium_ledger-0.1.0/tests/test_tickets.py +98 -0
- aglium_ledger-0.1.0/uv.lock +968 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aglium",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Aglium",
|
|
5
|
+
"url": "https://aglium.com"
|
|
6
|
+
},
|
|
7
|
+
"metadata": {
|
|
8
|
+
"description": "Plugins from Aglium. Ledger: cross-repository context for a change, before it merges."
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "ledger",
|
|
13
|
+
"source": "./plugin",
|
|
14
|
+
"description": "Before you push: what this change affects in the repositories you are not looking at, and whether someone needs to decide something first."
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
steps:
|
|
7
|
+
- uses: actions/checkout@v4
|
|
8
|
+
- uses: actions/setup-python@v5
|
|
9
|
+
with: { python-version: "3.12" }
|
|
10
|
+
- name: The client, with no database library installed
|
|
11
|
+
run: |
|
|
12
|
+
pip install '.[mcp]' pytest pytest-asyncio respx
|
|
13
|
+
python -c "import importlib.util as u, sys; sys.exit(bool(u.find_spec('sqlalchemy') or u.find_spec('ledger_server')))"
|
|
14
|
+
pytest -q tests
|
|
15
|
+
clean-install:
|
|
16
|
+
# What a new user's machine does: no client installed, the plugin's runner
|
|
17
|
+
# fetches it with uvx (from this checkout instead of the tagged release).
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
os: [ubuntu-latest, macos-latest]
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v6
|
|
25
|
+
- name: Fetch with uvx, check prerequisites and coverage, replay
|
|
26
|
+
env:
|
|
27
|
+
# A directory, not git+file: a CI checkout has no origin/HEAD, and uv
|
|
28
|
+
# cannot resolve a git URL without a default branch.
|
|
29
|
+
LEDGER_PACKAGE: "aglium-ledger[mcp] @ file://${{ github.workspace }}"
|
|
30
|
+
run: |
|
|
31
|
+
set -eu
|
|
32
|
+
est=$(mktemp -d)
|
|
33
|
+
for r in orders billing; do
|
|
34
|
+
git init -q -b main "$est/$r"
|
|
35
|
+
git -C "$est/$r" -c user.email=a@example.com -c user.name=a commit -q --allow-empty -m start
|
|
36
|
+
done
|
|
37
|
+
printf 'org: acme\nproject_keys: [ABC]\n' > "$est/ledger.yml"
|
|
38
|
+
export LEDGER_CONFIG="$est/ledger.yml"
|
|
39
|
+
plugin/scripts/ledger-run.sh coverage --local-root "$est" --json > cov.json || true
|
|
40
|
+
python3 -c "
|
|
41
|
+
import json; d = json.load(open('cov.json'))
|
|
42
|
+
assert {r['repo'] for r in d['coverage']['repos']} == {'orders', 'billing'}, d
|
|
43
|
+
assert all(c['ok'] for c in d['prerequisites']), d['prerequisites']
|
|
44
|
+
"
|
|
45
|
+
plugin/scripts/ledger-run.sh replay --local-root "$est" --everyone --out "$est/r.md"
|
|
46
|
+
grep -q "## Nothing to replay" "$est/r.md"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
# Pushing a tag `vX.Y.Z` builds the package and attaches it to a GitHub release.
|
|
3
|
+
# Publishing to PyPI is off until the repository variable PUBLISH_TO_PYPI is
|
|
4
|
+
# `true` and PyPI trusted publishing is set up for `aglium-ledger` (workflow
|
|
5
|
+
# `release.yml`, environment `pypi`).
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with: { python-version: "3.12" }
|
|
18
|
+
- name: Tag must match the package version
|
|
19
|
+
run: |
|
|
20
|
+
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
21
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
22
|
+
echo "tag $GITHUB_REF_NAME does not match pyproject version $version" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
- run: pip install build
|
|
26
|
+
# Only the client is released here. The server ships as an image, privately.
|
|
27
|
+
- run: python -m build --outdir dist
|
|
28
|
+
- uses: actions/upload-artifact@v4
|
|
29
|
+
with: { name: dist, path: dist/ }
|
|
30
|
+
- name: Create the release
|
|
31
|
+
env:
|
|
32
|
+
GH_TOKEN: ${{ github.token }}
|
|
33
|
+
run: gh release create "$GITHUB_REF_NAME" dist/* --title "$GITHUB_REF_NAME" --generate-notes
|
|
34
|
+
pypi:
|
|
35
|
+
needs: build
|
|
36
|
+
if: vars.PUBLISH_TO_PYPI == 'true'
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
environment: pypi
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/download-artifact@v4
|
|
43
|
+
with: { name: dist, path: dist/ }
|
|
44
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Ledger client
|
|
2
|
+
|
|
3
|
+
Required Notice: Copyright (c) 2026 Aglium (https://aglium.com)
|
|
4
|
+
Licensor Line of Business: Ledger, cross-repository impact analysis for software changes (https://aglium.com)
|
|
5
|
+
|
|
6
|
+
The terms below are the PolyForm Shield License 1.0.0, unmodified.
|
|
7
|
+
|
|
8
|
+
# PolyForm Shield License 1.0.0
|
|
9
|
+
|
|
10
|
+
<https://polyformproject.org/licenses/shield/1.0.0>
|
|
11
|
+
|
|
12
|
+
## Acceptance
|
|
13
|
+
|
|
14
|
+
In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
|
|
15
|
+
|
|
16
|
+
## Copyright License
|
|
17
|
+
|
|
18
|
+
The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license).
|
|
19
|
+
|
|
20
|
+
## Distribution License
|
|
21
|
+
|
|
22
|
+
The licensor grants you an additional copyright license to distribute copies of the software. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license).
|
|
23
|
+
|
|
24
|
+
## Notices
|
|
25
|
+
|
|
26
|
+
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example:
|
|
27
|
+
|
|
28
|
+
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
|
29
|
+
|
|
30
|
+
## Changes and New Works License
|
|
31
|
+
|
|
32
|
+
The licensor grants you an additional copyright license to make changes and new works based on the software for any permitted purpose.
|
|
33
|
+
|
|
34
|
+
## Patent License
|
|
35
|
+
|
|
36
|
+
The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
|
|
37
|
+
|
|
38
|
+
## Noncompete
|
|
39
|
+
|
|
40
|
+
Any purpose is a permitted purpose, except for providing any product that competes with the software or any product the licensor or any of its affiliates provides using the software.
|
|
41
|
+
|
|
42
|
+
## Competition
|
|
43
|
+
|
|
44
|
+
Goods and services compete even when they provide functionality through different kinds of interfaces or for different technical platforms. Applications can compete with services, libraries with plugins, frameworks with development tools, and so on, even if they're written in different programming languages or for different computer architectures. Goods and services compete even when provided free of charge. If you market a product as a practical substitute for the software or another product, it definitely competes.
|
|
45
|
+
|
|
46
|
+
## New Products
|
|
47
|
+
|
|
48
|
+
If you are using the software to provide a product that does not compete, but the licensor or any of its affiliates brings your product into competition by providing a new version of the software or another product using the software, you may continue using versions of the software available under these terms beforehand to provide your competing product, but not any later versions.
|
|
49
|
+
|
|
50
|
+
## Discontinued Products
|
|
51
|
+
|
|
52
|
+
You may begin using the software to compete with a product or service that the licensor or any of its affiliates has stopped providing, unless the licensor includes a plain-text line beginning with `Licensor Line of Business:` with the software that mentions that line of business. For example:
|
|
53
|
+
|
|
54
|
+
> Licensor Line of Business: YoyodyneCMS Content Management System (http://example.com/cms)
|
|
55
|
+
|
|
56
|
+
## Sales of Business
|
|
57
|
+
|
|
58
|
+
If the licensor or any of its affiliates sells a line of business developing the software or using the software to provide a product, the buyer can also enforce [Noncompete](#noncompete) for that product.
|
|
59
|
+
|
|
60
|
+
## Fair Use
|
|
61
|
+
|
|
62
|
+
You may have "fair use" rights for the software under the law. These terms do not limit them.
|
|
63
|
+
|
|
64
|
+
## No Other Rights
|
|
65
|
+
|
|
66
|
+
These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
|
|
67
|
+
|
|
68
|
+
## Patent Defense
|
|
69
|
+
|
|
70
|
+
If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
|
71
|
+
|
|
72
|
+
## Violations
|
|
73
|
+
|
|
74
|
+
The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice. Otherwise, all your licenses end immediately.
|
|
75
|
+
|
|
76
|
+
## No Liability
|
|
77
|
+
|
|
78
|
+
***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
|
|
79
|
+
|
|
80
|
+
## Definitions
|
|
81
|
+
|
|
82
|
+
The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
|
|
83
|
+
|
|
84
|
+
A **product** can be a good or service, or a combination of them.
|
|
85
|
+
|
|
86
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
87
|
+
|
|
88
|
+
**Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all its affiliates.
|
|
89
|
+
|
|
90
|
+
**Affiliates** means the other organizations than an organization has control over, is under the control of, or is under common control with.
|
|
91
|
+
|
|
92
|
+
**Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
|
93
|
+
|
|
94
|
+
**Your licenses** are all the licenses granted to you for the software under these terms.
|
|
95
|
+
|
|
96
|
+
**Use** means anything you do with the software requiring one of your licenses.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: aglium-ledger
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cross-repo impact brief for a change: the client, with no database
|
|
5
|
+
Project-URL: Homepage, https://aglium.com/platform/ledger-server.html
|
|
6
|
+
Project-URL: Source, https://github.com/Aglium/ledger-client
|
|
7
|
+
Author: Aglium
|
|
8
|
+
License-Expression: LicenseRef-PolyForm-Shield-1.0.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Requires-Dist: httpx>=0.27
|
|
12
|
+
Requires-Dist: pydantic>=2.7
|
|
13
|
+
Requires-Dist: pyyaml>=6.0
|
|
14
|
+
Requires-Dist: typer>=0.12
|
|
15
|
+
Provides-Extra: mcp
|
|
16
|
+
Requires-Dist: mcp<3,>=2.2; extra == 'mcp'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# Aglium Ledger client
|
|
20
|
+
|
|
21
|
+
**Your coding agent sees your repository. Ledger sees what your changes might affect elsewhere.**
|
|
22
|
+
|
|
23
|
+
Claude Code can work on the repository in front of it. The change often reaches further: another
|
|
24
|
+
team's branch on the same ticket, a service that reads the table you are changing, a design
|
|
25
|
+
decision whose open question this change quietly answers. Ledger reads the rest of your
|
|
26
|
+
organisation's repositories on your own machine and answers, for a branch: a recommendation,
|
|
27
|
+
the evidence behind it, and the people or decisions to involve.
|
|
28
|
+
|
|
29
|
+
Source-available · No Aglium account · No model calls by the client · Nothing sent to Aglium
|
|
30
|
+
|
|
31
|
+
## Would it have warned you? Replay your last merges
|
|
32
|
+
|
|
33
|
+
The quickest way to find out whether Ledger is useful to you is to run it on changes your team
|
|
34
|
+
already merged, as they were on the day each one started.
|
|
35
|
+
|
|
36
|
+
In Claude Code:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
/plugin marketplace add Aglium/ledger-client
|
|
40
|
+
/plugin install ledger@aglium
|
|
41
|
+
/ledger:setup # writes a config, checks prerequisites, says which repositories Ledger can see
|
|
42
|
+
/ledger:replay # your merged changes from the last 90 days; --everyone for your team's
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
From a terminal (Python 3.12+ and [uv](https://docs.astral.sh/uv/)):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv tool install 'aglium-ledger[mcp]'
|
|
49
|
+
cd ~/src/your-org/one-of-your-repos
|
|
50
|
+
aglium-ledger init # a config from this checkout; contacts nothing
|
|
51
|
+
aglium-ledger mirrors # optional: the rest of the organisation, as bare mirrors
|
|
52
|
+
aglium-ledger coverage # which repositories Ledger can see, and the gaps
|
|
53
|
+
aglium-ledger replay --everyone
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The replay writes `ledger-replay.md`. For each change it keeps three outcomes apart:
|
|
57
|
+
|
|
58
|
+
- **something to say**: other work or contracts this change affected, each with links to the
|
|
59
|
+
commits and files you can open to check it, and how strong the claim is (a *conflict*: it
|
|
60
|
+
removes something another repository reads, or cannot land with another change in flight; a
|
|
61
|
+
*dependency*: it faces another repository over a named topic or route; or a *correlation*,
|
|
62
|
+
such as a shared ticket);
|
|
63
|
+
- **nothing found**, by a replay that could see enough to say so;
|
|
64
|
+
- **could not see enough to tell**, which is never the same as safe.
|
|
65
|
+
|
|
66
|
+
There is no hindsight in it: every other repository is read at the commit its default branch
|
|
67
|
+
had on the day the change started, so nothing merged later can make a finding look smarter
|
|
68
|
+
than it was. More in [docs/replay.md](docs/replay.md).
|
|
69
|
+
|
|
70
|
+
You then say, for each finding, whether it was **useful**, **already known**, a **false
|
|
71
|
+
positive**, or something you **can't verify**. The answers stay on your machine
|
|
72
|
+
(`aglium-ledger feedback import ledger-replay.md`). If you want to share the result,
|
|
73
|
+
`aglium-ledger feedback summary --share` writes a file with the verdicts and hashed names, no
|
|
74
|
+
code, for you to read and send.
|
|
75
|
+
|
|
76
|
+
## The recommendation
|
|
77
|
+
|
|
78
|
+
| Verdict | Meaning |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `proceed` | Nothing found in what Ledger could see |
|
|
81
|
+
| `review_with` | Other work overlaps: name these repositories, branches and people in your review |
|
|
82
|
+
| `decision_needed` | An open decision governs this change; a person with authority decides first |
|
|
83
|
+
| `insufficient_data` | Ledger could not see enough. Unknown, never safe |
|
|
84
|
+
|
|
85
|
+
`aglium-ledger brief <repo> <branch>` gives it for any branch; `--json` for a CI gate or an
|
|
86
|
+
agent. Every answer names the repositories it read, so a quiet answer from a small estate is
|
|
87
|
+
not mistaken for a clean one.
|
|
88
|
+
|
|
89
|
+
## Commands
|
|
90
|
+
|
|
91
|
+
| Command | What it does |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `init` | Write a config from the current checkout |
|
|
94
|
+
| `mirrors` | Discover the organisation's repositories and keep bare mirrors of them |
|
|
95
|
+
| `coverage` | Which repositories Ledger can see, their history, the gaps, and the prerequisites |
|
|
96
|
+
| `replay` | Brief your recent merged changes as they were, to see whether Ledger would have helped |
|
|
97
|
+
| `feedback record / import / summary` | Your verdicts on findings, kept locally |
|
|
98
|
+
| `brief <repo> <ref>` | What a branch affects elsewhere |
|
|
99
|
+
| `explain <repo> <ref>` | Other in-flight work on a branch's ticket |
|
|
100
|
+
| `scan` | What the estate holds: repositories, contracts, work in flight |
|
|
101
|
+
| `comment` | Post a sibling comment on a pull request (used by the GitHub Action) |
|
|
102
|
+
| `mcp` | Run the MCP server for coding agents |
|
|
103
|
+
| `hook` | The push-time hook for Claude Code |
|
|
104
|
+
| `server` | What an organisation server adds, and how to connect to one |
|
|
105
|
+
|
|
106
|
+
## Use it from a coding agent
|
|
107
|
+
|
|
108
|
+
- **Claude Code:** the plugin in [`plugin/`](plugin/README.md) adds the skill, the MCP server,
|
|
109
|
+
a push-time hook and `/ledger:setup`, `/ledger:coverage`, `/ledger:brief`, `/ledger:replay`.
|
|
110
|
+
- **Any MCP client:** run `aglium-ledger mcp` as a stdio server. Tools: `brief`, `who_owns`,
|
|
111
|
+
`inflight_for`, `decisions_for`, `feedback`.
|
|
112
|
+
|
|
113
|
+
Installation on a clean machine, permissions and troubleshooting:
|
|
114
|
+
[docs/install.md](docs/install.md).
|
|
115
|
+
|
|
116
|
+
## On a pull request
|
|
117
|
+
|
|
118
|
+
The GitHub Action in this repository runs the client statelessly on each pull request and
|
|
119
|
+
comments with the full brief: a field or contract the change removes that another repository
|
|
120
|
+
reads, another change in flight that cannot land with it, who is on the other side of each
|
|
121
|
+
topic or route, and other work linked by ticket, by what it touches or by author. A branch
|
|
122
|
+
with no ticket key is briefed like any other. It is advisory and never fails a build; set
|
|
123
|
+
`mode: siblings` for the old ticket-only comment.
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
- uses: Aglium/ledger-client@v0.1.0
|
|
127
|
+
continue-on-error: true
|
|
128
|
+
with:
|
|
129
|
+
github-token: ${{ secrets.LEDGER_TOKEN }}
|
|
130
|
+
config: .github/ledger.yml
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Trust and data
|
|
134
|
+
|
|
135
|
+
The client runs on your machine. It reads your repositories with the credential git already
|
|
136
|
+
has, sends nothing to Aglium, makes no model calls, and puts no file contents in its answers:
|
|
137
|
+
they carry names, contracts, decisions and paths. Exactly what it reads, contacts and stores
|
|
138
|
+
is in [docs/trust.md](docs/trust.md).
|
|
139
|
+
|
|
140
|
+
## Client and server
|
|
141
|
+
|
|
142
|
+
The client is complete by itself and free to use. It reads the estate live, on demand, from
|
|
143
|
+
what is on your machine. What it does not do, alone:
|
|
144
|
+
|
|
145
|
+
- see repositories or branches that are not on this machine;
|
|
146
|
+
- answer in about 2 seconds (it reads again each time: seconds to tens of seconds);
|
|
147
|
+
- read topic wiring declared in infrastructure (ACLs, Terraform, AsyncAPI), only code and config;
|
|
148
|
+
- check every field of a contract (it checks the names a change removes, in consumers' code);
|
|
149
|
+
- see work merged to an integration branch and not released, or decisions on unmerged branches;
|
|
150
|
+
- say anything before code exists, or notice documentation a change leaves wrong;
|
|
151
|
+
- share findings and feedback with your team, or learn which verdicts were right.
|
|
152
|
+
|
|
153
|
+
An organisation server does all of these, for everyone, through the same plugin and commands.
|
|
154
|
+
Side by side: [docs/client-vs-server.md](docs/client-vs-server.md). `aglium-ledger server`
|
|
155
|
+
says the same from the command line.
|
|
156
|
+
|
|
157
|
+
## Licence
|
|
158
|
+
|
|
159
|
+
[PolyForm Shield License 1.0.0](LICENSE): source-available, not open source. Free to use,
|
|
160
|
+
change and redistribute, at work too; not to build a product that competes with Ledger. The
|
|
161
|
+
licence text is what counts. Copyright (c) 2026 Aglium, [aglium.com](https://aglium.com).
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Aglium Ledger client
|
|
2
|
+
|
|
3
|
+
**Your coding agent sees your repository. Ledger sees what your changes might affect elsewhere.**
|
|
4
|
+
|
|
5
|
+
Claude Code can work on the repository in front of it. The change often reaches further: another
|
|
6
|
+
team's branch on the same ticket, a service that reads the table you are changing, a design
|
|
7
|
+
decision whose open question this change quietly answers. Ledger reads the rest of your
|
|
8
|
+
organisation's repositories on your own machine and answers, for a branch: a recommendation,
|
|
9
|
+
the evidence behind it, and the people or decisions to involve.
|
|
10
|
+
|
|
11
|
+
Source-available · No Aglium account · No model calls by the client · Nothing sent to Aglium
|
|
12
|
+
|
|
13
|
+
## Would it have warned you? Replay your last merges
|
|
14
|
+
|
|
15
|
+
The quickest way to find out whether Ledger is useful to you is to run it on changes your team
|
|
16
|
+
already merged, as they were on the day each one started.
|
|
17
|
+
|
|
18
|
+
In Claude Code:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
/plugin marketplace add Aglium/ledger-client
|
|
22
|
+
/plugin install ledger@aglium
|
|
23
|
+
/ledger:setup # writes a config, checks prerequisites, says which repositories Ledger can see
|
|
24
|
+
/ledger:replay # your merged changes from the last 90 days; --everyone for your team's
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
From a terminal (Python 3.12+ and [uv](https://docs.astral.sh/uv/)):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv tool install 'aglium-ledger[mcp]'
|
|
31
|
+
cd ~/src/your-org/one-of-your-repos
|
|
32
|
+
aglium-ledger init # a config from this checkout; contacts nothing
|
|
33
|
+
aglium-ledger mirrors # optional: the rest of the organisation, as bare mirrors
|
|
34
|
+
aglium-ledger coverage # which repositories Ledger can see, and the gaps
|
|
35
|
+
aglium-ledger replay --everyone
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The replay writes `ledger-replay.md`. For each change it keeps three outcomes apart:
|
|
39
|
+
|
|
40
|
+
- **something to say**: other work or contracts this change affected, each with links to the
|
|
41
|
+
commits and files you can open to check it, and how strong the claim is (a *conflict*: it
|
|
42
|
+
removes something another repository reads, or cannot land with another change in flight; a
|
|
43
|
+
*dependency*: it faces another repository over a named topic or route; or a *correlation*,
|
|
44
|
+
such as a shared ticket);
|
|
45
|
+
- **nothing found**, by a replay that could see enough to say so;
|
|
46
|
+
- **could not see enough to tell**, which is never the same as safe.
|
|
47
|
+
|
|
48
|
+
There is no hindsight in it: every other repository is read at the commit its default branch
|
|
49
|
+
had on the day the change started, so nothing merged later can make a finding look smarter
|
|
50
|
+
than it was. More in [docs/replay.md](docs/replay.md).
|
|
51
|
+
|
|
52
|
+
You then say, for each finding, whether it was **useful**, **already known**, a **false
|
|
53
|
+
positive**, or something you **can't verify**. The answers stay on your machine
|
|
54
|
+
(`aglium-ledger feedback import ledger-replay.md`). If you want to share the result,
|
|
55
|
+
`aglium-ledger feedback summary --share` writes a file with the verdicts and hashed names, no
|
|
56
|
+
code, for you to read and send.
|
|
57
|
+
|
|
58
|
+
## The recommendation
|
|
59
|
+
|
|
60
|
+
| Verdict | Meaning |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `proceed` | Nothing found in what Ledger could see |
|
|
63
|
+
| `review_with` | Other work overlaps: name these repositories, branches and people in your review |
|
|
64
|
+
| `decision_needed` | An open decision governs this change; a person with authority decides first |
|
|
65
|
+
| `insufficient_data` | Ledger could not see enough. Unknown, never safe |
|
|
66
|
+
|
|
67
|
+
`aglium-ledger brief <repo> <branch>` gives it for any branch; `--json` for a CI gate or an
|
|
68
|
+
agent. Every answer names the repositories it read, so a quiet answer from a small estate is
|
|
69
|
+
not mistaken for a clean one.
|
|
70
|
+
|
|
71
|
+
## Commands
|
|
72
|
+
|
|
73
|
+
| Command | What it does |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `init` | Write a config from the current checkout |
|
|
76
|
+
| `mirrors` | Discover the organisation's repositories and keep bare mirrors of them |
|
|
77
|
+
| `coverage` | Which repositories Ledger can see, their history, the gaps, and the prerequisites |
|
|
78
|
+
| `replay` | Brief your recent merged changes as they were, to see whether Ledger would have helped |
|
|
79
|
+
| `feedback record / import / summary` | Your verdicts on findings, kept locally |
|
|
80
|
+
| `brief <repo> <ref>` | What a branch affects elsewhere |
|
|
81
|
+
| `explain <repo> <ref>` | Other in-flight work on a branch's ticket |
|
|
82
|
+
| `scan` | What the estate holds: repositories, contracts, work in flight |
|
|
83
|
+
| `comment` | Post a sibling comment on a pull request (used by the GitHub Action) |
|
|
84
|
+
| `mcp` | Run the MCP server for coding agents |
|
|
85
|
+
| `hook` | The push-time hook for Claude Code |
|
|
86
|
+
| `server` | What an organisation server adds, and how to connect to one |
|
|
87
|
+
|
|
88
|
+
## Use it from a coding agent
|
|
89
|
+
|
|
90
|
+
- **Claude Code:** the plugin in [`plugin/`](plugin/README.md) adds the skill, the MCP server,
|
|
91
|
+
a push-time hook and `/ledger:setup`, `/ledger:coverage`, `/ledger:brief`, `/ledger:replay`.
|
|
92
|
+
- **Any MCP client:** run `aglium-ledger mcp` as a stdio server. Tools: `brief`, `who_owns`,
|
|
93
|
+
`inflight_for`, `decisions_for`, `feedback`.
|
|
94
|
+
|
|
95
|
+
Installation on a clean machine, permissions and troubleshooting:
|
|
96
|
+
[docs/install.md](docs/install.md).
|
|
97
|
+
|
|
98
|
+
## On a pull request
|
|
99
|
+
|
|
100
|
+
The GitHub Action in this repository runs the client statelessly on each pull request and
|
|
101
|
+
comments with the full brief: a field or contract the change removes that another repository
|
|
102
|
+
reads, another change in flight that cannot land with it, who is on the other side of each
|
|
103
|
+
topic or route, and other work linked by ticket, by what it touches or by author. A branch
|
|
104
|
+
with no ticket key is briefed like any other. It is advisory and never fails a build; set
|
|
105
|
+
`mode: siblings` for the old ticket-only comment.
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
- uses: Aglium/ledger-client@v0.1.0
|
|
109
|
+
continue-on-error: true
|
|
110
|
+
with:
|
|
111
|
+
github-token: ${{ secrets.LEDGER_TOKEN }}
|
|
112
|
+
config: .github/ledger.yml
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Trust and data
|
|
116
|
+
|
|
117
|
+
The client runs on your machine. It reads your repositories with the credential git already
|
|
118
|
+
has, sends nothing to Aglium, makes no model calls, and puts no file contents in its answers:
|
|
119
|
+
they carry names, contracts, decisions and paths. Exactly what it reads, contacts and stores
|
|
120
|
+
is in [docs/trust.md](docs/trust.md).
|
|
121
|
+
|
|
122
|
+
## Client and server
|
|
123
|
+
|
|
124
|
+
The client is complete by itself and free to use. It reads the estate live, on demand, from
|
|
125
|
+
what is on your machine. What it does not do, alone:
|
|
126
|
+
|
|
127
|
+
- see repositories or branches that are not on this machine;
|
|
128
|
+
- answer in about 2 seconds (it reads again each time: seconds to tens of seconds);
|
|
129
|
+
- read topic wiring declared in infrastructure (ACLs, Terraform, AsyncAPI), only code and config;
|
|
130
|
+
- check every field of a contract (it checks the names a change removes, in consumers' code);
|
|
131
|
+
- see work merged to an integration branch and not released, or decisions on unmerged branches;
|
|
132
|
+
- say anything before code exists, or notice documentation a change leaves wrong;
|
|
133
|
+
- share findings and feedback with your team, or learn which verdicts were right.
|
|
134
|
+
|
|
135
|
+
An organisation server does all of these, for everyone, through the same plugin and commands.
|
|
136
|
+
Side by side: [docs/client-vs-server.md](docs/client-vs-server.md). `aglium-ledger server`
|
|
137
|
+
says the same from the command line.
|
|
138
|
+
|
|
139
|
+
## Licence
|
|
140
|
+
|
|
141
|
+
[PolyForm Shield License 1.0.0](LICENSE): source-available, not open source. Free to use,
|
|
142
|
+
change and redistribute, at work too; not to build a product that competes with Ledger. The
|
|
143
|
+
licence text is what counts. Copyright (c) 2026 Aglium, [aglium.com](https://aglium.com).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: "Ledger — what this change affects elsewhere"
|
|
2
|
+
description: >
|
|
3
|
+
Comment on a pull request with what it affects in OTHER repositories: fields or
|
|
4
|
+
contracts it removes that another repository reads, changes in flight that cannot
|
|
5
|
+
land with it, who is on the other side of each topic or route, and other work
|
|
6
|
+
linked by ticket, by what it touches or by author. Works without a ticket key.
|
|
7
|
+
Stateless: queries the forges directly, needs no database or service.
|
|
8
|
+
inputs:
|
|
9
|
+
github-token:
|
|
10
|
+
description: "Token with read access to the org's repos and write access to PR comments."
|
|
11
|
+
required: true
|
|
12
|
+
config:
|
|
13
|
+
description: "Path to your ledger.yml, relative to the calling repository."
|
|
14
|
+
required: true
|
|
15
|
+
post:
|
|
16
|
+
description: "Post the comment. Set to 'false' to render it into the log only."
|
|
17
|
+
required: false
|
|
18
|
+
default: "true"
|
|
19
|
+
mode:
|
|
20
|
+
description: "'brief' (default): the full brief. 'siblings': only work sharing the ticket, as before."
|
|
21
|
+
required: false
|
|
22
|
+
default: "brief"
|
|
23
|
+
runs:
|
|
24
|
+
using: composite
|
|
25
|
+
steps:
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- name: Install
|
|
31
|
+
shell: bash
|
|
32
|
+
# A composite action already has its own files at action_path, so it never
|
|
33
|
+
# needs to check itself out -- which also means no org or repo name is
|
|
34
|
+
# hardcoded here. Whoever hosts the action, it installs from itself.
|
|
35
|
+
run: pip install -e "${{ github.action_path }}"
|
|
36
|
+
- name: Remember scans between runs
|
|
37
|
+
# What each repository's default branch says is read again only when
|
|
38
|
+
# that branch moves. Names and directions only; no code is cached.
|
|
39
|
+
uses: actions/cache@v4
|
|
40
|
+
with:
|
|
41
|
+
path: ~/.cache/ledger/scan
|
|
42
|
+
key: ledger-scan-${{ github.run_id }}
|
|
43
|
+
restore-keys: ledger-scan-
|
|
44
|
+
- name: Comment
|
|
45
|
+
shell: bash
|
|
46
|
+
env:
|
|
47
|
+
LEDGER_GITHUB_TOKEN: ${{ inputs.github-token }}
|
|
48
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
49
|
+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
50
|
+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
51
|
+
REPO: ${{ github.repository }}
|
|
52
|
+
run: |
|
|
53
|
+
# --live keeps this a single step with no infrastructure behind it.
|
|
54
|
+
# Failure here must never fail someone else's PR: this is advisory, and
|
|
55
|
+
# a tool that red-builds a branch on its own outage gets removed.
|
|
56
|
+
aglium-ledger comment "$REPO" "$PR_NUMBER" \
|
|
57
|
+
--ref "$HEAD_REF" \
|
|
58
|
+
--head-sha "$HEAD_SHA" \
|
|
59
|
+
--config "${{ github.workspace }}/${{ inputs.config }}" \
|
|
60
|
+
--live \
|
|
61
|
+
$([ "${{ inputs.mode }}" = "siblings" ] && echo "--siblings-only" || echo "--brief") \
|
|
62
|
+
$([ "${{ inputs.post }}" = "true" ] && echo "--post" || echo "--dry-run") \
|
|
63
|
+
|| echo "::warning::ledger could not produce a comment; continuing"
|