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.
Files changed (117) hide show
  1. aglium_ledger-0.1.0/.claude-plugin/marketplace.json +17 -0
  2. aglium_ledger-0.1.0/.github/workflows/ci.yml +46 -0
  3. aglium_ledger-0.1.0/.github/workflows/release.yml +44 -0
  4. aglium_ledger-0.1.0/.gitignore +9 -0
  5. aglium_ledger-0.1.0/LICENSE +96 -0
  6. aglium_ledger-0.1.0/PKG-INFO +161 -0
  7. aglium_ledger-0.1.0/README.md +143 -0
  8. aglium_ledger-0.1.0/action.yml +63 -0
  9. aglium_ledger-0.1.0/config/ledger.example.yml +151 -0
  10. aglium_ledger-0.1.0/docs/client-vs-server.md +81 -0
  11. aglium_ledger-0.1.0/docs/install.md +73 -0
  12. aglium_ledger-0.1.0/docs/replay.md +80 -0
  13. aglium_ledger-0.1.0/docs/trust.md +59 -0
  14. aglium_ledger-0.1.0/plugin/.claude-plugin/plugin.json +17 -0
  15. aglium_ledger-0.1.0/plugin/.mcp.json +8 -0
  16. aglium_ledger-0.1.0/plugin/LICENSE +96 -0
  17. aglium_ledger-0.1.0/plugin/README.md +97 -0
  18. aglium_ledger-0.1.0/plugin/commands/brief.md +25 -0
  19. aglium_ledger-0.1.0/plugin/commands/coverage.md +13 -0
  20. aglium_ledger-0.1.0/plugin/commands/replay.md +41 -0
  21. aglium_ledger-0.1.0/plugin/commands/setup.md +34 -0
  22. aglium_ledger-0.1.0/plugin/hooks/hooks.json +17 -0
  23. aglium_ledger-0.1.0/plugin/scripts/ledger-run.sh +69 -0
  24. aglium_ledger-0.1.0/plugin/skills/ledger/SKILL.md +109 -0
  25. aglium_ledger-0.1.0/pyproject.toml +35 -0
  26. aglium_ledger-0.1.0/src/ledger/__init__.py +0 -0
  27. aglium_ledger-0.1.0/src/ledger/adapters/__init__.py +0 -0
  28. aglium_ledger-0.1.0/src/ledger/adapters/vcs/__init__.py +0 -0
  29. aglium_ledger-0.1.0/src/ledger/adapters/vcs/base.py +277 -0
  30. aglium_ledger-0.1.0/src/ledger/adapters/vcs/bitbucket.py +126 -0
  31. aglium_ledger-0.1.0/src/ledger/adapters/vcs/fake.py +120 -0
  32. aglium_ledger-0.1.0/src/ledger/adapters/vcs/github.py +194 -0
  33. aglium_ledger-0.1.0/src/ledger/adapters/vcs/localgit.py +704 -0
  34. aglium_ledger-0.1.0/src/ledger/bench.py +252 -0
  35. aglium_ledger-0.1.0/src/ledger/cli.py +1084 -0
  36. aglium_ledger-0.1.0/src/ledger/config.py +202 -0
  37. aglium_ledger-0.1.0/src/ledger/core/__init__.py +0 -0
  38. aglium_ledger-0.1.0/src/ledger/core/bases.py +32 -0
  39. aglium_ledger-0.1.0/src/ledger/core/bindings.py +160 -0
  40. aglium_ledger-0.1.0/src/ledger/core/breaks.py +266 -0
  41. aglium_ledger-0.1.0/src/ledger/core/brief.py +848 -0
  42. aglium_ledger-0.1.0/src/ledger/core/card.py +111 -0
  43. aglium_ledger-0.1.0/src/ledger/core/check.py +94 -0
  44. aglium_ledger-0.1.0/src/ledger/core/contracts.py +472 -0
  45. aglium_ledger-0.1.0/src/ledger/core/credentials.py +80 -0
  46. aglium_ledger-0.1.0/src/ledger/core/decision.py +263 -0
  47. aglium_ledger-0.1.0/src/ledger/core/declarations.py +252 -0
  48. aglium_ledger-0.1.0/src/ledger/core/docs.py +134 -0
  49. aglium_ledger-0.1.0/src/ledger/core/effect.py +442 -0
  50. aglium_ledger-0.1.0/src/ledger/core/estate.py +219 -0
  51. aglium_ledger-0.1.0/src/ledger/core/evidence.py +156 -0
  52. aglium_ledger-0.1.0/src/ledger/core/gate.py +30 -0
  53. aglium_ledger-0.1.0/src/ledger/core/grounding.py +97 -0
  54. aglium_ledger-0.1.0/src/ledger/core/join.py +128 -0
  55. aglium_ledger-0.1.0/src/ledger/core/naming.py +74 -0
  56. aglium_ledger-0.1.0/src/ledger/core/overlap.py +293 -0
  57. aglium_ledger-0.1.0/src/ledger/core/rank.py +272 -0
  58. aglium_ledger-0.1.0/src/ledger/core/redact.py +130 -0
  59. aglium_ledger-0.1.0/src/ledger/core/render.py +146 -0
  60. aglium_ledger-0.1.0/src/ledger/core/tickets.py +63 -0
  61. aglium_ledger-0.1.0/src/ledger/coverage.py +204 -0
  62. aglium_ledger-0.1.0/src/ledger/feedback.py +213 -0
  63. aglium_ledger-0.1.0/src/ledger/init.py +174 -0
  64. aglium_ledger-0.1.0/src/ledger/pipeline/__init__.py +0 -0
  65. aglium_ledger-0.1.0/src/ledger/pipeline/collect.py +226 -0
  66. aglium_ledger-0.1.0/src/ledger/pipeline/decisions.py +80 -0
  67. aglium_ledger-0.1.0/src/ledger/pipeline/mirrors.py +183 -0
  68. aglium_ledger-0.1.0/src/ledger/pipeline/overlap_scan.py +135 -0
  69. aglium_ledger-0.1.0/src/ledger/pipeline/scan.py +206 -0
  70. aglium_ledger-0.1.0/src/ledger/pipeline/signals.py +80 -0
  71. aglium_ledger-0.1.0/src/ledger/pipeline/wiring.py +180 -0
  72. aglium_ledger-0.1.0/src/ledger/remote.py +47 -0
  73. aglium_ledger-0.1.0/src/ledger/replay.py +791 -0
  74. aglium_ledger-0.1.0/src/ledger/service.py +1326 -0
  75. aglium_ledger-0.1.0/src/ledger/surfaces/__init__.py +0 -0
  76. aglium_ledger-0.1.0/src/ledger/surfaces/hook.py +800 -0
  77. aglium_ledger-0.1.0/src/ledger/surfaces/mcp.py +250 -0
  78. aglium_ledger-0.1.0/src/ledger/upgrade.py +106 -0
  79. aglium_ledger-0.1.0/tests/adapters/test_vcs_contract.py +532 -0
  80. aglium_ledger-0.1.0/tests/conftest.py +9 -0
  81. aglium_ledger-0.1.0/tests/lab_estate.py +312 -0
  82. aglium_ledger-0.1.0/tests/test_bases.py +26 -0
  83. aglium_ledger-0.1.0/tests/test_bench.py +58 -0
  84. aglium_ledger-0.1.0/tests/test_beyond_tickets.py +160 -0
  85. aglium_ledger-0.1.0/tests/test_bindings.py +77 -0
  86. aglium_ledger-0.1.0/tests/test_boundary.py +46 -0
  87. aglium_ledger-0.1.0/tests/test_breaks.py +293 -0
  88. aglium_ledger-0.1.0/tests/test_brief.py +527 -0
  89. aglium_ledger-0.1.0/tests/test_check.py +67 -0
  90. aglium_ledger-0.1.0/tests/test_cli.py +207 -0
  91. aglium_ledger-0.1.0/tests/test_cli_render.py +33 -0
  92. aglium_ledger-0.1.0/tests/test_client_surfaces.py +154 -0
  93. aglium_ledger-0.1.0/tests/test_collect.py +213 -0
  94. aglium_ledger-0.1.0/tests/test_contracts.py +227 -0
  95. aglium_ledger-0.1.0/tests/test_coverage_feedback.py +134 -0
  96. aglium_ledger-0.1.0/tests/test_credentials.py +141 -0
  97. aglium_ledger-0.1.0/tests/test_decision.py +116 -0
  98. aglium_ledger-0.1.0/tests/test_declarations.py +403 -0
  99. aglium_ledger-0.1.0/tests/test_effect.py +208 -0
  100. aglium_ledger-0.1.0/tests/test_estate.py +178 -0
  101. aglium_ledger-0.1.0/tests/test_gate.py +94 -0
  102. aglium_ledger-0.1.0/tests/test_grounding.py +80 -0
  103. aglium_ledger-0.1.0/tests/test_hook.py +728 -0
  104. aglium_ledger-0.1.0/tests/test_init.py +131 -0
  105. aglium_ledger-0.1.0/tests/test_join.py +94 -0
  106. aglium_ledger-0.1.0/tests/test_mirrors.py +291 -0
  107. aglium_ledger-0.1.0/tests/test_naming.py +88 -0
  108. aglium_ledger-0.1.0/tests/test_overlap.py +189 -0
  109. aglium_ledger-0.1.0/tests/test_plugin.py +120 -0
  110. aglium_ledger-0.1.0/tests/test_rank.py +74 -0
  111. aglium_ledger-0.1.0/tests/test_redact.py +89 -0
  112. aglium_ledger-0.1.0/tests/test_render.py +82 -0
  113. aglium_ledger-0.1.0/tests/test_replay.py +371 -0
  114. aglium_ledger-0.1.0/tests/test_scan_cache.py +126 -0
  115. aglium_ledger-0.1.0/tests/test_secrets.py +28 -0
  116. aglium_ledger-0.1.0/tests/test_tickets.py +98 -0
  117. 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,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .pytest_cache/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .DS_Store
9
+ ledger-replay.md
@@ -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"