Catalog, roadmap, and CI-gated status tracking for ledger properties (#1225) - #1226
Conversation
c2c871d to
bcee109
Compare
bcee109 to
7942b71
Compare
There was a problem hiding this comment.
Pull request overview
Introduces a repo-managed catalog + generated roadmap for tracking formal ledger properties across Conway and Dijkstra, with property status derived from Agda source and enforced via a CI drift gate to prevent roadmap/issue drift from proofs.
Changes:
- Adds a YAML property catalog and an ADR describing the tracking model and workflow.
- Adds Python tooling to (a) derive property status from Agda and regenerate the roadmap, and (b) sync/render GitHub issues for coordination.
- Adds a CI workflow to run the no-network “roadmap is current” check, publishes the roadmap via mkdocs, and adds README badges.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
scripts/python/scan_properties.py |
Derives status from Agda/filesystem and regenerates canonical + mkdocs roadmap; --check gates drift. |
scripts/python/README.md |
Documents the property-tracking scripts and intended workflows. |
scripts/python/gh_project_render.py |
Renders a GitHub-issues coordination view for catalog-referenced issues. |
scripts/python/gh_project_populate.py |
Seeds GitHub issues/labels from the catalog and writes created issue numbers back. |
README.md |
Adds CI/nightly/properties-check workflow badges. |
docs/notes/properties.yaml |
Adds the version-controlled property catalog (no status field; status derived). |
docs/notes/ledger-properties-roadmap.md |
Adds the generated, human-facing roadmap dashboard. |
docs/notes/0001-ledger-property-tracking.md |
Adds ADR describing rationale, conventions, and reconciliation/workflow. |
build-tools/static/mkdocs/mkdocs.yml |
Publishes the roadmap page in mkdocs navigation. |
build-tools/static/mkdocs/docs/ledger-properties-roadmap.md |
Adds the mkdocs-site copy of the generated roadmap (intended byte-identical). |
.github/workflows/properties-check.yml |
Adds CI drift gate running scan_properties.py --check on relevant changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
534803f to
9926708
Compare
|
Rebase refresh (2026-08-05). Force-pushed
Generated by Claude Code |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (1)
scripts/python/scan_properties.py:227
validate_catalog()records missing/duplicate ids as errors, butmain()still unconditionally callsevaluate(p)and indexesp["id"]. If the catalog ever contains an entry withoutid(orpropertiesis missing), the script will crash with a traceback instead of reporting the catalog error and exiting cleanly with code 1 (breaking the intended CI gate UX).
results = {}
for p in cat["properties"]:
status, path, probs = evaluate(p)
results[p["id"]] = {"status": status, "path": path}
problems.extend(probs)
|
Copilot's 2026-08-06 re-review came back clean (no new comments). Its one suppressed note — |
ce4c57c to
cd861ec
Compare
cd861ec to
110e06b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Suppressed comments (6)
.github/workflows/properties-check.yml:19
- The path filter does not include this workflow file itself. A future PR that only changes or breaks
properties-check.ymlwill not run the gate it modifies. Include.github/workflows/properties-check.ymlin the filtered paths.
- 'scripts/python/scan_properties.py'
scripts/python/gh_project_populate.py:229
--erais documented as limiting the run to one era, but label setup still uses every catalog era with--force. Thus--era dijkstraalso creates or updatesera:conway. Filter this loop consistently with the umbrella and property loops.
for era in meta["eras"]:
ensure_label(gh, f"era:{era}", LABEL_COLORS["era"], f"{era} era")
scripts/python/gh_project_populate.py:257
- Existing tracked issues are skipped before any label reconciliation. Because
status:*is applied only when creating an issue, a later Agda transition such asstated→provedleaves the GitHub label—and any label-backed coordination view—permanently stale after rerunning this script. Reconcile derived labels on existing issues while preserving unrelated labels, or avoid publishing status labels.
if p.get("issues"):
continue # already tracked
scripts/python/gh_project_populate.py:264
- Issue numbers are only held in memory until the entire creation loop finishes. If a later
create_issueraises, earlier issues already exist remotely but none of their numbers are written back, so rerunning creates duplicates. Persist each successful write-back atomically before the next creation, or save accumulated updates on failure.
if num:
created += 1
text = set_issue_numbers(text, p["id"], [num])
scripts/python/README.md:47
- Issue #1225's acceptance criteria require the catalog↔issues sync to be run once, including labels and initial issues, but this PR's notes state that population remains unapplied and the Dijkstra catalog entries still have no issues. Since the PR says it implements #1225, either complete that initial seeding before merge or explicitly leave/update the issue scope rather than presenting this criterion as complete.
Creates the per-era umbrella tracking issues, the `property` / `era:*` /
`status:*` / `sts:*` labels, and one issue per catalog entry that has no issue
yet; best-effort links them as sub-issues of the umbrella; then writes the new
issue numbers back into `properties.yaml` (comment-preserving, targeted edits).
scripts/python/scan_properties.py:120
- Only
id,era, andstsare validated, and even the list item is assumed to be a mapping. Consequentlyproperties: [null]crashes here, while an entry missingtitleor using a non-stringmodulepasses validation and crashes later in evaluation/rendering instead of producing the documented catalog error. Validate each item and all downstream-required field types before evaluation.
for p in cat.get("properties", []):
pid = p.get("id")
|
Triage of the six suppressed notes in Copilot's 2026-08-07 review (the visible roadmap-drift comment was already fixed and resolved): Fixed — all five code notes were legit
Addressed by scoping, not code |
293fe39 to
8611193
Compare
b98b11c to
0414afb
Compare
0414afb to
7d0ff0c
Compare
There was a problem hiding this comment.
I feel an ADR should be about the decisions we take on the architecture of the formal spec and not of meta-level issues such as how we track which properties we have proven.
I'd suggest to split this file in two things
- move the "How to" and "Conventions" to CONTRIBUTING and,
- record the problem and the solution this PR solves as part of its description instead of a whole document inside the formal spec itself. I think we should strive to keep the repo as clean as possible to not fall into chaos. (Specially in the age of AI where generating text is free).
There was a problem hiding this comment.
Agreed and done in 0ad697e.
The ADR is gone, its How-to/Conventions are now a CONTRIBUTING section ('Tracking Properties of the Ledger'), and the problem/decision record moved into the PR description.
No flake change was needed: the default nix develop shell already carries python3 + PyYAML for the scanner; the two optional gh-sync scripts just need an authenticated gh.
0ad697e to
b2856b0
Compare
build-tools/properties.yaml is the single hand-curated inventory of ledger properties across the Conway and Dijkstra eras: identity, era, STS, Agda module, key defs, and tracking issues. It deliberately declares NO status — status is derived from the Agda by the scanner (next commit), so the bookkeeping cannot silently drift from the code. docs/adr/0001-ledger-property-tracking.md records the design: a three-layer model (Agda = truth for "proved", catalog = identity, GitHub issues = coordination), the derived-status vocabulary (idea/planned/stated/proved), property-module conventions, the workflow, and the initial reconciliation plan for #45's sub-issues. Part of #1225. AI-assisted: Claude Fable 5 (Anthropic)
build-tools/scripts/scan_properties.py derives each catalog entry's status from the Agda on disk (no module -> idea; module file absent -> planned; "coming soon" marker -> stated; otherwise -> proved) and regenerates the dashboard, build-tools/static/mkdocs/docs/ledger-properties-dashboard.md — a single generated file living in the mkdocs site tree. --check regenerates and fails on a stale dashboard or an invalid catalog (per-entry shape validation with clean error reports, exit 1); it is the no-network drift gate that CI runs. The Agda --safe typecheck in the main CI is what makes 'proved' mean proved. Part of #1225. AI-assisted: Claude Fable 5 (Anthropic)
catalog -> GitHub: build-tools/scripts/gh_project_populate.py seeds labels, per-era umbrella issues, and one issue per untracked catalog entry, writing new numbers back into the catalog immediately after each creation (an interrupted run loses nothing, so a rerun cannot file duplicates). For already-tracked issues it reconciles the derived status:* label — add current, drop stale, preserve everything else; issues shared by entries with differing derived statuses are skipped with a note. GitHub -> repo: build-tools/scripts/gh_project_render.py renders live open/closed/assignee state for every tracked issue into build-tools/static/mkdocs/docs/ledger-properties-issues.md, logging one ✅/❌ line per issue as it fetches. Formal status stays with the scanner; this view is coordination only. The initial seeding created the Conway trackers #1228–#1241 under umbrella #45 and the Dijkstra umbrella #1227; the ~15 Dijkstra port issues are deferred until the ports start. build-tools/scripts/README.md documents all three scripts. Part of #1225. AI-assisted: Claude Fable 5 (Anthropic)
The generated dashboard becomes the site's "Properties Dashboard" page, with its nav tab between Dijkstra and Appendix; the README gains CI and properties-check badges so the drift gate's status is visible at a glance. Part of #1225. AI-assisted: Claude Fable 5 (Anthropic)
Module names in the dashboard tables now link to the module's generated page on the site (the dashboard lives in the same directory); issue numbers link to the GitHub issues. A planned module is not on the branch, so it has no page — it stays plain text rather than a dangling link that would fail the strict mkdocs build. Verified every proved/stated catalog module is in the site's import closure (aggregators re-export them all), so every emitted link resolves. AI-assisted: Claude Fable 5 (Anthropic)
The Conway module listing predated the EventuallyRefunded (#414) and LastVoteApplied (#417, GOV + LEDGER) modules — their pages were built (they are in the site's import closure via the Properties aggregators) but unreachable from the sidebar. Also fix the GenMinspend label typo. Interface.TypeClasses and Ledger.Dijkstra.Specification.Computational are literate modules with no generated page (not in the import closure of Ledger.lagda.md), so they stay out of the nav. AI-assisted: Claude Fable 5 (Anthropic)
The rebase brought in PR #1289 (ChangePPGroup proved at the GOV level, LEDGER-level lift added), which flips the derived statuses: Conway is now 15 proved / 5 stated / 0 planned. The committed dashboard was not regenerated with the rebase, so the properties-check gate was failing on the previous head — exactly the drift it exists to catch.
Carlos: an ADR should record decisions about the architecture of the formal spec, not meta-level tooling. Accordingly: - the ADR's "How to" and "Conventions" content moves, condensed, to a new CONTRIBUTING.md section, "Tracking Properties of the Ledger", which also documents scan_properties.py and the two gh_project scripts, and notes that the default nix develop shell already provides the required python3 + PyYAML (the flake needs no change); - the problem/decision record moves to the PR description; - docs/adr/0001-ledger-property-tracking.md is deleted, and every reference to it (catalog header, scanner preamble, scripts README, populate issue bodies, workflow comment) now points at the CONTRIBUTING section. AI-assisted development: Claude Fable 5 (Anthropic)
be3cb32 to
2daee6b
Compare
carlostome
left a comment
There was a problem hiding this comment.
LGTM!
Maybe as a final comment, why don't we create a subfolder of build-tools/scripts (e.g., property-catalog) that holds the three scripts and the README?
This way is clear what the README is about.
Carlos suggested grouping the property-tracking tooling in a subdirectory of build-tools/scripts. Move scan_properties.py, gh_project_render.py, gh_project_populate.py, and their README into build-tools/scripts/property-tracking/ and update every reference: the scripts' usage headers and REPO_ROOT depth, the README's title and relative links, CONTRIBUTING.md, the properties-check workflow (run command and paths filter), the catalog header comment, and the two generated files (dashboard regenerated fresh; issues view path strings updated to match what the moved renderer emits). AI-assisted development: Claude Fable 5 (Anthropic)
Restructure the module conventions as separate bullets, spell out the add/state/prove workflow as numbered steps with the exact commands, and tighten the issue-sync prose.
Written back by gh_project_populate.py --era conway; regenerate the dashboard so the issues column shows it.
properties.yaml joins the scripts in build-tools/scripts/property- tracking/; the three scripts now locate it relative to their own directory. Update the path everywhere it is written out (script headers, issue-body footer, CONTRIBUTING, workflow header comment, issues-view preamble) and regenerate the dashboard, whose preamble embeds it. The workflow's paths filter entry for the catalog is dropped: the property-tracking/** glob already covers its new home. AI-assisted development: Claude Fable 5 (Anthropic)
A real populate run was silent for many seconds between its printed milestones. Announce every mutating gh call (📝 while running, then ✅ or ❌), confirm already-correct status labels during the reconciliation pass, and end with a ✅ done line. The renderer gains a fetch banner and icons on its verdict lines (it already logged one ✅/❌ per issue fetched); the scanner now lists every property with its derived-status badge and marks warnings⚠️ , errors ❌, and the final verdict ✅/❌. AI-assisted development: Claude Fable 5 (Anthropic)
Pick up #1312 (created by the conway populate run) and current open/closed state.
Description
A version-controlled tracking system for ledger properties across the Conway and Dijkstra eras (the machinery for #1225; the one-time issue-seeding run is deliberately deferred — see notes for reviewers).
The idea in one line:
and CI fails whenever the committed bookkeeping disagrees with the code.
Why (the problem this solves)
Three representations of each property used to drift apart: the Agda (the only artifact that can say whether something is proved), the GitHub issues (#45 and its sub-issues), and our prose/mental model. Concrete evidence found while auditing for this PR: #413 was already proved (
updatePropDeps≡, feedingUTXO-minspend) yet sat open and unlinked; #414 was easy to mistake for done but had no statement at all; several proved theorems (theGovDepsMatchand PoV families,PParamsWellFormed,ConstRwds,CredDepsEqualDomRwds,NoPropSameDReps) had no tracking issue; and the #45 comment-properties became Agda claims without ever becoming issues. Two readers could disagree about whether #413 or #414 was "the done one".The fix is a single source of truth per concern:
--safetypecheck;coming soonmarker = pending)src/**/Properties/**build-tools/scripts/property-tracking/properties.yamlbuild-tools/static/mkdocs/docs/ledger-properties-dashboard.mdAlternatives considered: treating issue open/closed state as the status (rejected: closing an issue by hand does not make a proof exist; the issue-sync scripts are kept for coordination, not truth), and a GitHub Projects board as the primary tracker (rejected: not version-controlled, and invisible to CI and to fresh clones; kept as an optional read-only view).
How the pieces fit
build-tools/scripts/property-tracking/properties.yaml: the human-curated catalog.build-tools/scripts/property-tracking/scan_properties.py: resolves each entry's module againstsrc/.This script classifies each entry as one of the following (and regenerates
ledger-properties-dashboard):idea;planned;coming soonmarker →stated;proved(the Agda--safetypecheck in main CI is what makesprovedmean proved).build-tools/static/mkdocs/docs/ledger-properties-dashboard.md: the generated dashboard (per-era summary + status tables).Generated directly in the mkdocs site tree and published as the Properties Dashboard page (a single copy — there is no separate canonical/site pair).
.github/workflows/properties-check.yml: the no-network CI gate.scan_properties.py --checkfails if the catalog is invalid or the committed dashboard is stale.gh_project_populate.py/gh_project_render.py: optional GitHub-sync scripts (run locally withgh).Seed issues/labels from the catalog, and render
build-tools/static/mkdocs/docs/ledger-properties-issues.md, the live open/closed/assignee coordination view. Coordination only---never a source of status truth.The scripts are documented in
build-tools/scripts/property-tracking/README.md; conventions and day-to-day workflow live in CONTRIBUTING.md, in the new "Tracking Properties of the Ledger" section.Try it out!
Status is derived, not declared: watch the gate catch a "lie" (from
nix develop):python3 build-tools/scripts/property-tracking/scan_properties.py --check # OK: bookkeeping agrees with the Agda# regenerate the dashboard python3 build-tools/scripts/property-tracking/scan_properties.py# undo the experiment git checkout -- src build-toolsSee the dashboard on the docs site (top-level Properties Dashboard tab, next to Home):
or incrementally, reusing an existing
_build:nix develop→fls-shake mkdocs→ servedist/mkdocs.With an authenticated
gh, the issue side:# refresh the issues view from live GitHub; git diff shows drift python3 build-tools/scripts/property-tracking/gh_project_render.py# preview catalog→GitHub seeding (leave unapplied; see notes) python3 build-tools/scripts/property-tracking/gh_project_populate.py --dry-runContents
Docs / tooling / CI only:
src/is byte-identical tomaster. The catalog, the generated dashboard and issues view (both live in the mkdocs site tree), the three scripts + their README, the CI workflow, the mkdocs nav entry, README badges, and a new CONTRIBUTING.md section ("Tracking Properties of the Ledger") with the conventions and workflow.Current state: Conway 15 proved / 5 stated / 2 idea; Dijkstra 15 port ideas.
Notes for reviewers
Issues under #45 were already reconciled while building this:
gh_project_populate.pyis deliberately left un-applied; its label scheme (era:conway,status:*,sts:*) differs from the repo's existing labels (era: conway), and applying would also file the 15 Dijkstra port issues.Follow-up: reconcile the conventions before the first real run; the port issues get filed as ports start.
The optional read-only GitHub Projects v2 board is left as a follow-up improvement (set up once by hand; the Projects v2 API is out of scope for the in-repo scripts).
Part of #1225.
Checklist
CHANGELOG.md(n/a — docs/tooling/CI only)src/is byte-identical tomaster)