epsiloneridani-progress 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 (39) hide show
  1. epsiloneridani_progress-0.1.0/LICENSE +201 -0
  2. epsiloneridani_progress-0.1.0/PKG-INFO +174 -0
  3. epsiloneridani_progress-0.1.0/README.md +164 -0
  4. epsiloneridani_progress-0.1.0/epsiloneridani_progress.egg-info/PKG-INFO +174 -0
  5. epsiloneridani_progress-0.1.0/epsiloneridani_progress.egg-info/SOURCES.txt +37 -0
  6. epsiloneridani_progress-0.1.0/epsiloneridani_progress.egg-info/dependency_links.txt +1 -0
  7. epsiloneridani_progress-0.1.0/epsiloneridani_progress.egg-info/entry_points.txt +2 -0
  8. epsiloneridani_progress-0.1.0/epsiloneridani_progress.egg-info/top_level.txt +1 -0
  9. epsiloneridani_progress-0.1.0/progress/__init__.py +0 -0
  10. epsiloneridani_progress-0.1.0/progress/announce.py +139 -0
  11. epsiloneridani_progress-0.1.0/progress/apply.py +528 -0
  12. epsiloneridani_progress-0.1.0/progress/cli.py +211 -0
  13. epsiloneridani_progress-0.1.0/progress/context.py +150 -0
  14. epsiloneridani_progress-0.1.0/progress/docs.py +258 -0
  15. epsiloneridani_progress-0.1.0/progress/facts.py +246 -0
  16. epsiloneridani_progress-0.1.0/progress/files.py +717 -0
  17. epsiloneridani_progress-0.1.0/progress/gate.py +533 -0
  18. epsiloneridani_progress-0.1.0/progress/gh.py +164 -0
  19. epsiloneridani_progress-0.1.0/progress/layers.py +157 -0
  20. epsiloneridani_progress-0.1.0/progress/plan.py +503 -0
  21. epsiloneridani_progress-0.1.0/progress/prompts/progress.md +178 -0
  22. epsiloneridani_progress-0.1.0/progress/prompts/status.md +101 -0
  23. epsiloneridani_progress-0.1.0/progress/window.py +189 -0
  24. epsiloneridani_progress-0.1.0/progress/zulip.py +157 -0
  25. epsiloneridani_progress-0.1.0/pyproject.toml +26 -0
  26. epsiloneridani_progress-0.1.0/setup.cfg +4 -0
  27. epsiloneridani_progress-0.1.0/tests/test_apply_announce.py +609 -0
  28. epsiloneridani_progress-0.1.0/tests/test_collect.py +122 -0
  29. epsiloneridani_progress-0.1.0/tests/test_context.py +151 -0
  30. epsiloneridani_progress-0.1.0/tests/test_docs.py +251 -0
  31. epsiloneridani_progress-0.1.0/tests/test_facts.py +268 -0
  32. epsiloneridani_progress-0.1.0/tests/test_files.py +408 -0
  33. epsiloneridani_progress-0.1.0/tests/test_gate.py +690 -0
  34. epsiloneridani_progress-0.1.0/tests/test_in_flight.py +127 -0
  35. epsiloneridani_progress-0.1.0/tests/test_layers.py +209 -0
  36. epsiloneridani_progress-0.1.0/tests/test_prompts.py +153 -0
  37. epsiloneridani_progress-0.1.0/tests/test_supersede.py +201 -0
  38. epsiloneridani_progress-0.1.0/tests/test_window.py +519 -0
  39. epsiloneridani_progress-0.1.0/tests/test_window_resolution.py +157 -0
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright {yyyy} {name of copyright owner}
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.4
2
+ Name: epsiloneridani-progress
3
+ Version: 0.1.0
4
+ Summary: Decide, generate, and publish per-roadmap progress reports for EpsilonEridani.
5
+ License: Apache-2.0
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Dynamic: license-file
10
+
11
+ # EpsilonEridaniProgress
12
+
13
+ Progress reporting for [EpsilonEridani](https://github.com/eic/EpsilonEridani): what has actually
14
+ been achieved on each roadmap, written for a human to read in a minute.
15
+
16
+ Each roadmap directory in
17
+ [EpsilonEridaniRoadmaps](https://github.com/eic/EpsilonEridaniRoadmaps) carries two generated files:
18
+
19
+ - **`STATUS.md`** — a snapshot, rewritten whole on each update. It says which parts of the roadmap
20
+ are done and sketches the frontier, headed by the commit it describes.
21
+ - **`PROGRESS.md`** — an append-only log. Each section covers one window of merged PRs as a few
22
+ holistic paragraphs, emphasising named theorems rather than listing every PR.
23
+
24
+ New `PROGRESS.md` sections are announced in the **EpsilonEridani > Progress logs** Zulip topic.
25
+
26
+ ## Why this repo exists
27
+
28
+ The rubrics-and-machinery split of
29
+ [EpsilonEridaniReview](https://github.com/eic/EpsilonEridaniReview), applied to reporting: the
30
+ prompts and the tooling live here, the output lands in EpsilonEridaniRoadmaps, and
31
+ [EpsilonEridaniWorker](https://github.com/eic/EpsilonEridaniWorker) drives it.
32
+
33
+ The design rule is that **a model only ever writes prose**. Every decision — whether an update is
34
+ due, which roadmap it covers, which PRs are in the window, and what mathematics actually landed —
35
+ is made by tested Python before any model starts, and the git and pull-request work afterwards is
36
+ done by tested Python too.
37
+
38
+ ## The commands
39
+
40
+ ```
41
+ epsiloneridani-progress due is an update due? (one API call, no clone)
42
+ epsiloneridani-progress plan --roadmap-dir DIR pick the roadmap and the PR window
43
+ epsiloneridani-progress facts --plan FILE what declarations actually landed in the window
44
+ epsiloneridani-progress apply --plan FILE ... write the files, open the PR (resumable)
45
+ epsiloneridani-progress announce --section FILE post a new section to Zulip (idempotent)
46
+ ```
47
+
48
+ `due` is the only one that runs often; it exits 75 ("no progress") when nothing is due, matching
49
+ the worker's convention. `plan` runs at most once a day.
50
+
51
+ ## The window cursor is a SHA, on the docs-tracking branch
52
+
53
+ A window is the half-open commit range `(from_sha, to_sha]` on EpsilonEridani's **`docgen`** branch, where
54
+ `from_sha` is the `to_sha` of the previous `PROGRESS.md` section.
55
+
56
+ `docgen` nominates the most recent commit on `main` whose API documentation has been published, and
57
+ the window ends at **the commit the published documentation actually reports** — read from the site
58
+ itself, since the deploy is independent and the branch can sit ahead of it. Ending at the branch tip
59
+ instead would record a cursor covering work the report never described, and because the next window
60
+ starts after that cursor, the work in between would never be reported at all.
61
+
62
+ The cost is latency: a report describes the project as of the last published docs build rather than
63
+ the tip. That is the right trade for a document whose whole purpose is to be read, and the header
64
+ records the exact commit, so nothing is misdated.
65
+
66
+ Links are not computed by this project at all, and neither are declaration names. Both are read from
67
+ doc-gen4's own published output — the declaration index and each module page, which carry the exact
68
+ name, kind, source file and line range — so a link resolves because it was read from the page it
69
+ points at. `git blame` over those line ranges is what decides whether a declaration belongs to the
70
+ window.
71
+
72
+ There is deliberately no Lean parser here. Qualifying a name correctly means resolving `namespace`
73
+ against `section`, `end`, `open ... in` and `_root_`, and many real declarations (projections,
74
+ constructors, `deriving` output) are never written in the source at all. An approximation gets most
75
+ names right, which is the worst outcome available: the wrong ones are indistinguishable from the
76
+ right ones, and a link built from a wrong name is a plausible dead link. PR numbers come from the squash-merge commit
77
+ subjects in that range and are attributed by their `roadmap/<Area>` label.
78
+
79
+ Wall-clock time is used only for display. A cursor made of timestamps would be wrong: a worker
80
+ clock running fast advances it past PRs whose merge times then fall *before* the stored cursor, and
81
+ those PRs are never reported at all.
82
+
83
+ ## The coverage header
84
+
85
+ A status snapshot may carry a second machine header beside `epsiloneridani-status:v1`:
86
+
87
+ ```text
88
+ <!--epsiloneridani-status:v1 {"roadmap":"EllipticCurves","to_sha":"…","ts":"…"}-->
89
+ <!--epsiloneridani-coverage:v1 {"layers":[{"id":"Layer 0","remaining":"Weil reciprocity","state":"partial"},{"id":"Layer 1","state":"done"},…],"readme_sha":"…","roadmap":"EllipticCurves","to_sha":"…"}-->
90
+ # Status: EllipticCurves
91
+ ```
92
+
93
+ It is the report's verdict on each layer of the roadmap, in a form a script can read; the prose
94
+ says the same things, but nothing can aggregate prose across forty roadmaps. The consumer is the
95
+ EpsilonEridani site's Progress page (`scripts/roadmap_progress.py` in the EpsilonEridani repository).
96
+
97
+ **Wire schema.** `roadmap` is the status header's area, or `Area/Child` for a sub-roadmap (below);
98
+ `to_sha` equals the status header's; `readme_sha` is the SHA-256 of the README the layers were read
99
+ from; `layers` is a non-empty list (at most 64) of `{"id", "state", "remaining"?}` with `id` a short
100
+ label (`Layer 3`, `Lane G`, `L0A`), `state` one of `done`, `partial`, `untouched`, `unassessed` (the
101
+ material said nothing), and `remaining` an optional one-line note of at most 200 characters with no
102
+ angle brackets. No other keys anywhere. Keys are sorted and the JSON is compact;
103
+ `files.require_coverage` is the one definition, and both the worker and the gate run it.
104
+
105
+ **Trust boundary.** The model supplies only the assessments, as JSON inside a fenced
106
+ ```` ```coverage ```` block at the end of its status prose. Code supplies everything else: `plan`
107
+ records the README's layer headings (`Layer` / `Lane` / `Part` / `Stage`, or `L0A`-style labels)
108
+ and `readme_sha` in the plan; `apply` removes the block, validates the payload against the schema,
109
+ checks it names every listed layer exactly once and nothing else, and writes the header; the gate
110
+ treats the headers as part of the canonical prefix (the lines after the status header, nowhere else)
111
+ and re-validates them. The gate proves the header's shape, never its truth, and cannot check
112
+ `readme_sha` (it never checks out the roadmap repository): the header is a claim, not a certificate.
113
+
114
+ **Missing block.** A README with no layer headings gives a plan with no layers and a report with no
115
+ header. When the plan lists layers, the worker refuses a body with no block, just as it refuses a
116
+ block that is malformed, not JSON, or does not fit the plan: a new report changes the report hash,
117
+ which retires the site's hand transcription of the old report, so publishing it headerless would
118
+ turn assessed layers into unassessed ones. The model always has `unassessed` for a layer it cannot
119
+ judge. The gate is deliberately more lenient and still accepts a status file without the header,
120
+ which is what every report written before the header existed looks like.
121
+
122
+ **README hash.** `readme_sha` binds an assessment to the specification it was made against: layer
123
+ ids alone do not, since a layer's requirements can change under an unchanged heading. The consumer
124
+ refuses a header whose hash does not match the README it reads and shows those layers as
125
+ unassessed with that reason.
126
+
127
+ **Umbrella areas.** An area whose directories include sub-roadmaps (RepresentationTheory's twelve)
128
+ is one labelled area with one report, so that report is the only account of its children. A
129
+ sub-roadmap is a directory directly below the area with a `README.md` and a `Suggested.lean`, the
130
+ consumer's rule. `plan` lists each one with layer headings under `sub_roadmaps`, with its
131
+ `Area/Child` id, its README path and its own `readme_sha`; the model reads those READMEs and answers
132
+ with a JSON object keyed by roadmap id instead of an array; and `apply` writes one coverage header
133
+ per sub-roadmap, each bound to its own README, after the area's own header (if its README has
134
+ layers) and in ascending order of name. The gate holds each to the same schema, requires
135
+ `Area/Child` with an alphanumeric child directly below the status file's own area, caps them at 32,
136
+ and treats all of them as part of the canonical prefix. A consumer that does not know sub-roadmap
137
+ headers ignores them: their `roadmap` never equals a row's own name.
138
+
139
+ **Rollout.** Merge here, then bump the two pins in EpsilonEridaniRoadmaps's `progress-*.yml` workflows and
140
+ the worker's `PROGRESS_REF` to the same SHA, together: the generator and the gate must run one
141
+ version. Reports written before the bump simply have no header.
142
+
143
+ **Leaving the hand transcriptions.** Today the site reads layer states from a hand-transcribed
144
+ file in the EpsilonEridani repository (`scripts/roadmap_coverage.json`), each entry bound to the exact
145
+ report it was read from. Nothing is backfilled: an area's transcription stays valid until its next
146
+ report, which after the bump carries the headers (the worker refuses one without them when there
147
+ are layers), and the site then reads those instead and the transcription is retired. That holds
148
+ for the RepresentationTheory children too, all at once, at the umbrella's next report, provided
149
+ the site reads sub-roadmap headers by then. Each entry can be deleted from the transcription file
150
+ once its roadmap's report carries a header.
151
+
152
+ ## Trust boundary
153
+
154
+ `STATUS.md` and `PROGRESS.md` are **machine-owned, and their prose is not security-validated**.
155
+
156
+ The merge gate proves the *shape* of a generated update — its paths, its cursor, that it is a
157
+ byte-exact append, and that the head being merged is the head that was validated. It cannot prove
158
+ that the prose is true. Anyone may open a EpsilonEridani PR whose description contains prompt-injection
159
+ text; once that PR merges legitimately, its description reaches the writing model.
160
+
161
+ The mitigations reduce the risk and are not claimed to remove it: the model is grounded in
162
+ mechanically-extracted declaration names rather than author prose, PR bodies are delimited and
163
+ size-capped, reserved `epsiloneridani-*:v1` markers are rejected in model output, and Zulip mentions are
164
+ defused. Read these two files as a machine's summary, not as reviewed roadmap content.
165
+
166
+ **The blast radius is two markdown files AND a Zulip message.** Every merged section is posted to
167
+ **EpsilonEridani > Progress logs** automatically, so accepted prose reaches an audience outside the
168
+ repository. The post is treated as data -- mentions and bare `#123` linkifiers are defused, the
169
+ message is size-capped, it links both the appended log and the current roadmap status, and it is
170
+ idempotent on a stable per-window id -- but it is a second sink and the threat model has to say so.
171
+
172
+ ## Licence
173
+
174
+ Apache-2.0.
@@ -0,0 +1,164 @@
1
+ # EpsilonEridaniProgress
2
+
3
+ Progress reporting for [EpsilonEridani](https://github.com/eic/EpsilonEridani): what has actually
4
+ been achieved on each roadmap, written for a human to read in a minute.
5
+
6
+ Each roadmap directory in
7
+ [EpsilonEridaniRoadmaps](https://github.com/eic/EpsilonEridaniRoadmaps) carries two generated files:
8
+
9
+ - **`STATUS.md`** — a snapshot, rewritten whole on each update. It says which parts of the roadmap
10
+ are done and sketches the frontier, headed by the commit it describes.
11
+ - **`PROGRESS.md`** — an append-only log. Each section covers one window of merged PRs as a few
12
+ holistic paragraphs, emphasising named theorems rather than listing every PR.
13
+
14
+ New `PROGRESS.md` sections are announced in the **EpsilonEridani > Progress logs** Zulip topic.
15
+
16
+ ## Why this repo exists
17
+
18
+ The rubrics-and-machinery split of
19
+ [EpsilonEridaniReview](https://github.com/eic/EpsilonEridaniReview), applied to reporting: the
20
+ prompts and the tooling live here, the output lands in EpsilonEridaniRoadmaps, and
21
+ [EpsilonEridaniWorker](https://github.com/eic/EpsilonEridaniWorker) drives it.
22
+
23
+ The design rule is that **a model only ever writes prose**. Every decision — whether an update is
24
+ due, which roadmap it covers, which PRs are in the window, and what mathematics actually landed —
25
+ is made by tested Python before any model starts, and the git and pull-request work afterwards is
26
+ done by tested Python too.
27
+
28
+ ## The commands
29
+
30
+ ```
31
+ epsiloneridani-progress due is an update due? (one API call, no clone)
32
+ epsiloneridani-progress plan --roadmap-dir DIR pick the roadmap and the PR window
33
+ epsiloneridani-progress facts --plan FILE what declarations actually landed in the window
34
+ epsiloneridani-progress apply --plan FILE ... write the files, open the PR (resumable)
35
+ epsiloneridani-progress announce --section FILE post a new section to Zulip (idempotent)
36
+ ```
37
+
38
+ `due` is the only one that runs often; it exits 75 ("no progress") when nothing is due, matching
39
+ the worker's convention. `plan` runs at most once a day.
40
+
41
+ ## The window cursor is a SHA, on the docs-tracking branch
42
+
43
+ A window is the half-open commit range `(from_sha, to_sha]` on EpsilonEridani's **`docgen`** branch, where
44
+ `from_sha` is the `to_sha` of the previous `PROGRESS.md` section.
45
+
46
+ `docgen` nominates the most recent commit on `main` whose API documentation has been published, and
47
+ the window ends at **the commit the published documentation actually reports** — read from the site
48
+ itself, since the deploy is independent and the branch can sit ahead of it. Ending at the branch tip
49
+ instead would record a cursor covering work the report never described, and because the next window
50
+ starts after that cursor, the work in between would never be reported at all.
51
+
52
+ The cost is latency: a report describes the project as of the last published docs build rather than
53
+ the tip. That is the right trade for a document whose whole purpose is to be read, and the header
54
+ records the exact commit, so nothing is misdated.
55
+
56
+ Links are not computed by this project at all, and neither are declaration names. Both are read from
57
+ doc-gen4's own published output — the declaration index and each module page, which carry the exact
58
+ name, kind, source file and line range — so a link resolves because it was read from the page it
59
+ points at. `git blame` over those line ranges is what decides whether a declaration belongs to the
60
+ window.
61
+
62
+ There is deliberately no Lean parser here. Qualifying a name correctly means resolving `namespace`
63
+ against `section`, `end`, `open ... in` and `_root_`, and many real declarations (projections,
64
+ constructors, `deriving` output) are never written in the source at all. An approximation gets most
65
+ names right, which is the worst outcome available: the wrong ones are indistinguishable from the
66
+ right ones, and a link built from a wrong name is a plausible dead link. PR numbers come from the squash-merge commit
67
+ subjects in that range and are attributed by their `roadmap/<Area>` label.
68
+
69
+ Wall-clock time is used only for display. A cursor made of timestamps would be wrong: a worker
70
+ clock running fast advances it past PRs whose merge times then fall *before* the stored cursor, and
71
+ those PRs are never reported at all.
72
+
73
+ ## The coverage header
74
+
75
+ A status snapshot may carry a second machine header beside `epsiloneridani-status:v1`:
76
+
77
+ ```text
78
+ <!--epsiloneridani-status:v1 {"roadmap":"EllipticCurves","to_sha":"…","ts":"…"}-->
79
+ <!--epsiloneridani-coverage:v1 {"layers":[{"id":"Layer 0","remaining":"Weil reciprocity","state":"partial"},{"id":"Layer 1","state":"done"},…],"readme_sha":"…","roadmap":"EllipticCurves","to_sha":"…"}-->
80
+ # Status: EllipticCurves
81
+ ```
82
+
83
+ It is the report's verdict on each layer of the roadmap, in a form a script can read; the prose
84
+ says the same things, but nothing can aggregate prose across forty roadmaps. The consumer is the
85
+ EpsilonEridani site's Progress page (`scripts/roadmap_progress.py` in the EpsilonEridani repository).
86
+
87
+ **Wire schema.** `roadmap` is the status header's area, or `Area/Child` for a sub-roadmap (below);
88
+ `to_sha` equals the status header's; `readme_sha` is the SHA-256 of the README the layers were read
89
+ from; `layers` is a non-empty list (at most 64) of `{"id", "state", "remaining"?}` with `id` a short
90
+ label (`Layer 3`, `Lane G`, `L0A`), `state` one of `done`, `partial`, `untouched`, `unassessed` (the
91
+ material said nothing), and `remaining` an optional one-line note of at most 200 characters with no
92
+ angle brackets. No other keys anywhere. Keys are sorted and the JSON is compact;
93
+ `files.require_coverage` is the one definition, and both the worker and the gate run it.
94
+
95
+ **Trust boundary.** The model supplies only the assessments, as JSON inside a fenced
96
+ ```` ```coverage ```` block at the end of its status prose. Code supplies everything else: `plan`
97
+ records the README's layer headings (`Layer` / `Lane` / `Part` / `Stage`, or `L0A`-style labels)
98
+ and `readme_sha` in the plan; `apply` removes the block, validates the payload against the schema,
99
+ checks it names every listed layer exactly once and nothing else, and writes the header; the gate
100
+ treats the headers as part of the canonical prefix (the lines after the status header, nowhere else)
101
+ and re-validates them. The gate proves the header's shape, never its truth, and cannot check
102
+ `readme_sha` (it never checks out the roadmap repository): the header is a claim, not a certificate.
103
+
104
+ **Missing block.** A README with no layer headings gives a plan with no layers and a report with no
105
+ header. When the plan lists layers, the worker refuses a body with no block, just as it refuses a
106
+ block that is malformed, not JSON, or does not fit the plan: a new report changes the report hash,
107
+ which retires the site's hand transcription of the old report, so publishing it headerless would
108
+ turn assessed layers into unassessed ones. The model always has `unassessed` for a layer it cannot
109
+ judge. The gate is deliberately more lenient and still accepts a status file without the header,
110
+ which is what every report written before the header existed looks like.
111
+
112
+ **README hash.** `readme_sha` binds an assessment to the specification it was made against: layer
113
+ ids alone do not, since a layer's requirements can change under an unchanged heading. The consumer
114
+ refuses a header whose hash does not match the README it reads and shows those layers as
115
+ unassessed with that reason.
116
+
117
+ **Umbrella areas.** An area whose directories include sub-roadmaps (RepresentationTheory's twelve)
118
+ is one labelled area with one report, so that report is the only account of its children. A
119
+ sub-roadmap is a directory directly below the area with a `README.md` and a `Suggested.lean`, the
120
+ consumer's rule. `plan` lists each one with layer headings under `sub_roadmaps`, with its
121
+ `Area/Child` id, its README path and its own `readme_sha`; the model reads those READMEs and answers
122
+ with a JSON object keyed by roadmap id instead of an array; and `apply` writes one coverage header
123
+ per sub-roadmap, each bound to its own README, after the area's own header (if its README has
124
+ layers) and in ascending order of name. The gate holds each to the same schema, requires
125
+ `Area/Child` with an alphanumeric child directly below the status file's own area, caps them at 32,
126
+ and treats all of them as part of the canonical prefix. A consumer that does not know sub-roadmap
127
+ headers ignores them: their `roadmap` never equals a row's own name.
128
+
129
+ **Rollout.** Merge here, then bump the two pins in EpsilonEridaniRoadmaps's `progress-*.yml` workflows and
130
+ the worker's `PROGRESS_REF` to the same SHA, together: the generator and the gate must run one
131
+ version. Reports written before the bump simply have no header.
132
+
133
+ **Leaving the hand transcriptions.** Today the site reads layer states from a hand-transcribed
134
+ file in the EpsilonEridani repository (`scripts/roadmap_coverage.json`), each entry bound to the exact
135
+ report it was read from. Nothing is backfilled: an area's transcription stays valid until its next
136
+ report, which after the bump carries the headers (the worker refuses one without them when there
137
+ are layers), and the site then reads those instead and the transcription is retired. That holds
138
+ for the RepresentationTheory children too, all at once, at the umbrella's next report, provided
139
+ the site reads sub-roadmap headers by then. Each entry can be deleted from the transcription file
140
+ once its roadmap's report carries a header.
141
+
142
+ ## Trust boundary
143
+
144
+ `STATUS.md` and `PROGRESS.md` are **machine-owned, and their prose is not security-validated**.
145
+
146
+ The merge gate proves the *shape* of a generated update — its paths, its cursor, that it is a
147
+ byte-exact append, and that the head being merged is the head that was validated. It cannot prove
148
+ that the prose is true. Anyone may open a EpsilonEridani PR whose description contains prompt-injection
149
+ text; once that PR merges legitimately, its description reaches the writing model.
150
+
151
+ The mitigations reduce the risk and are not claimed to remove it: the model is grounded in
152
+ mechanically-extracted declaration names rather than author prose, PR bodies are delimited and
153
+ size-capped, reserved `epsiloneridani-*:v1` markers are rejected in model output, and Zulip mentions are
154
+ defused. Read these two files as a machine's summary, not as reviewed roadmap content.
155
+
156
+ **The blast radius is two markdown files AND a Zulip message.** Every merged section is posted to
157
+ **EpsilonEridani > Progress logs** automatically, so accepted prose reaches an audience outside the
158
+ repository. The post is treated as data -- mentions and bare `#123` linkifiers are defused, the
159
+ message is size-capped, it links both the appended log and the current roadmap status, and it is
160
+ idempotent on a stable per-window id -- but it is a second sink and the threat model has to say so.
161
+
162
+ ## Licence
163
+
164
+ Apache-2.0.