masterwork 0.0.2__tar.gz → 0.0.4__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.
- {masterwork-0.0.2/masterwork.egg-info → masterwork-0.0.4}/PKG-INFO +25 -19
- {masterwork-0.0.2 → masterwork-0.0.4}/README.md +10 -4
- {masterwork-0.0.2 → masterwork-0.0.4}/README.pypi.md +24 -18
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/__init__.py +1 -1
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/__main__.py +33 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/ceremony.py +13 -4
- masterwork-0.0.4/masterwork/starter/script.json +19 -0
- masterwork-0.0.4/masterwork/starter/tales.md +77 -0
- {masterwork-0.0.2 → masterwork-0.0.4/masterwork.egg-info}/PKG-INFO +25 -19
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork.egg-info/SOURCES.txt +3 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/pyproject.toml +2 -2
- masterwork-0.0.4/tests/test_badges.py +75 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_ceremony.py +17 -5
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_cli.py +16 -1
- {masterwork-0.0.2 → masterwork-0.0.4}/LICENSE +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/blind_label.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/campaign.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/cells.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/gate.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/line.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/measure.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/pairs.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/retain.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/rubric-example.json +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork/seal.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork.egg-info/dependency_links.txt +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork.egg-info/entry_points.txt +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork.egg-info/requires.txt +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/masterwork.egg-info/top_level.txt +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/setup.cfg +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_blind_label.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_campaign.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_cells.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_examples.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_gate.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_line.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_measure.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_packaging.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_pairs.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_retain.py +0 -0
- {masterwork-0.0.2 → masterwork-0.0.4}/tests/test_seal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: masterwork
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A production line for agent character: a sitting the model speaks for itself in, sealed candidates, frozen gates, and a verdict it is not allowed to give itself.
|
|
5
5
|
Author: Codechu
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -25,11 +25,11 @@ License-File: LICENSE
|
|
|
25
25
|
Requires-Dist: journeyman-bench>=0.2.1
|
|
26
26
|
Dynamic: license-file
|
|
27
27
|
|
|
28
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
28
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/banner.jpg" alt="A craftsman slides a sealed folio across his bench onto the guild's empty judging table; earlier pieces stand turned to the wall behind him." width="100%"></p>
|
|
29
29
|
|
|
30
30
|
# Masterwork
|
|
31
31
|
|
|
32
|
-
[](#run-it) [](https://github.com/codechu/masterwork/blob/v0.0.
|
|
32
|
+
[](#run-it) [](https://github.com/codechu/masterwork/blob/v0.0.4/LICENSE) [](https://github.com/codechu/masterwork/blob/v0.0.4/CONTRIBUTING.md#ground-rules) [](#status)
|
|
33
33
|
|
|
34
34
|
*Makes a character for a model. Does not score it.*
|
|
35
35
|
|
|
@@ -61,6 +61,8 @@ masterwork # what it is, and its stages
|
|
|
61
61
|
masterwork gate gates/ # check the gate templates
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
Start-to-finish, one command at a time: [`docs/first-run.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/first-run.md).
|
|
65
|
+
|
|
64
66
|
Or from a clone, with nothing installed at all:
|
|
65
67
|
|
|
66
68
|
```bash
|
|
@@ -115,7 +117,7 @@ record, and exits in a way that says *waiting*, not *broken*.
|
|
|
115
117
|
| `HELD_WITHOUT_PURPOSE` | the spec does not say what the run decides | write `purpose`; if you cannot say what changes on each outcome, the run is not ready |
|
|
116
118
|
| `HELD_AT_SEAL` | the piece is unreproducible, or the deployed copy differs | fix the seal or the deployment — never run "just to see" |
|
|
117
119
|
| `HELD_AT_COMPLETENESS` | the grid is short | find the missing cells; if proceeding anyway, declare it |
|
|
118
|
-
| `HELD_FOR_LABELLING` | labels are missing, empty, or written for an older version of a cell | get them with [`masterwork/blind_label.py`](https://github.com/codechu/masterwork/blob/v0.0.
|
|
120
|
+
| `HELD_FOR_LABELLING` | labels are missing, empty, or written for an older version of a cell | get them with [`masterwork/blind_label.py`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/blind_label.py); the line is waiting, not broken |
|
|
119
121
|
| `HELD_AT_MEASUREMENT` | the benchmark says the score is not comparable | judge from a separate endpoint, or declare and keep the stamp |
|
|
120
122
|
| `HELD_AT_GATE` | a threshold sits inside its own noise | the gate is void; measure the band, write a new gate |
|
|
121
123
|
| `HELD_WITHOUT_MEASUREMENT` | a rule names an axis and nothing measured it | judge with the benchmark, or declare the run diagnostic — a gate that was checked is not a gate that was applied |
|
|
@@ -173,7 +175,7 @@ deleted, because deciding what to remove is not the line's call.
|
|
|
173
175
|
|
|
174
176
|
## Making a piece
|
|
175
177
|
|
|
176
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
178
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/ceremony.jpg" alt="A figure writes alone by lantern light; every earlier work in the room is draped and turned away, and a stick of sealing wax lies unused on the table." width="520"></p>
|
|
177
179
|
|
|
178
180
|
`pipeline/ceremony.py` runs the sitting. Four properties are the recipe rather
|
|
179
181
|
than implementation detail, and each was paid for:
|
|
@@ -220,10 +222,10 @@ You get the line and its gates, the shape of the sitting, and the method
|
|
|
220
222
|
written down: how a tale differs from an instruction, where a tale comes from,
|
|
221
223
|
what a question has to do, why instructions come last, and how to tag a tale
|
|
222
224
|
whose effect cannot be separated. That is
|
|
223
|
-
[`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.
|
|
225
|
+
[`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.4/corpus/GROWING.md), and it is the part that transfers.
|
|
224
226
|
|
|
225
227
|
You do not get a corpus. Three pairs come with the repository in
|
|
226
|
-
[`
|
|
228
|
+
[`masterwork/starter/`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/starter/) so the machinery runs end to end and the
|
|
227
229
|
form is unambiguous; they are a beginning and they are meant to be outgrown.
|
|
228
230
|
The corpus is the work, and it has to be yours: a tale is written from
|
|
229
231
|
something **you** measured and got wrong, and a corpus assembled from someone
|
|
@@ -264,7 +266,7 @@ Not automatable, and this is the honest part:
|
|
|
264
266
|
|
|
265
267
|
## Status
|
|
266
268
|
|
|
267
|
-
|
|
269
|
+
Published, and not proven. Our own corpus has eighteen tales and took
|
|
268
270
|
months. It has produced eighteen candidates and none of them is ahead of the
|
|
269
271
|
agent already running: the closest comparison came back `UNRESOLVED` rather
|
|
270
272
|
than won — the difference was smaller than the gate could resolve, which is not
|
|
@@ -279,13 +281,17 @@ the position — a masterwork is submitted, and it can be refused.
|
|
|
279
281
|
|
|
280
282
|
| document | when you need it |
|
|
281
283
|
|---|---|
|
|
282
|
-
| [`
|
|
283
|
-
| [`
|
|
284
|
-
| [`docs/
|
|
285
|
-
| [`docs/
|
|
286
|
-
| [`
|
|
287
|
-
| [`
|
|
288
|
-
| [
|
|
284
|
+
| [`docs/first-run.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/first-run.md) | **start here** — nothing to a sealed candidate, one command at a time |
|
|
285
|
+
| [`AGENTS.md`](https://github.com/codechu/masterwork/blob/v0.0.4/AGENTS.md) | before running anything — the refusals, the vocabulary, and the four things not to do. Read it whether or not you are an agent |
|
|
286
|
+
| [`docs/ceremony.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/ceremony.md) | making a piece: the four properties of the sitting, and the script that drives it |
|
|
287
|
+
| [`docs/seal.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/seal.md) | the five fields a candidate carries, and what to do when the gate refuses one |
|
|
288
|
+
| [`docs/run-spec.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/run-spec.md) | writing the JSON for one run, field by field |
|
|
289
|
+
| [`docs/pairs.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/pairs.md) | turning a scored run into training data, and what a run leaves behind |
|
|
290
|
+
| [`docs/campaigns.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/campaigns.md) | repeating an arm to measure a band, or comparing two arms against it |
|
|
291
|
+
| [`docs/gates.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/gates.md) | writing a threshold that can hold — start from [`gates/succession.md`](https://github.com/codechu/masterwork/blob/v0.0.4/gates/succession.md) |
|
|
292
|
+
| [`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.4/corpus/GROWING.md) | before writing a single tale — the shape is in [`masterwork/starter/`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/starter/) |
|
|
293
|
+
| [`CHANGELOG.md`](https://github.com/codechu/masterwork/blob/v0.0.4/CHANGELOG.md) | every entry names the incident that caused it |
|
|
294
|
+
| [assets/README.md](https://github.com/codechu/masterwork/blob/v0.0.4/assets/README.md) | you need a fifth image that matches the four |
|
|
289
295
|
|
|
290
296
|
<details>
|
|
291
297
|
<summary>Layout</summary>
|
|
@@ -303,10 +309,10 @@ runs/ local run directories (ignored — heavy and re-derivable)
|
|
|
303
309
|
|
|
304
310
|
---
|
|
305
311
|
|
|
306
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
312
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/icon.png" alt="Masterwork guild seal — a struck M hallmark inside a rope ring" width="96"></p>
|
|
307
313
|
|
|
308
|
-
Contributing: [CONTRIBUTING.md](https://github.com/codechu/masterwork/blob/v0.0.
|
|
309
|
-
[CHANGELOG.md](https://github.com/codechu/masterwork/blob/v0.0.
|
|
310
|
-
[Apache License 2.0](https://github.com/codechu/masterwork/blob/v0.0.
|
|
314
|
+
Contributing: [CONTRIBUTING.md](https://github.com/codechu/masterwork/blob/v0.0.4/CONTRIBUTING.md) · Changelog:
|
|
315
|
+
[CHANGELOG.md](https://github.com/codechu/masterwork/blob/v0.0.4/CHANGELOG.md) · Licensed under the
|
|
316
|
+
[Apache License 2.0](https://github.com/codechu/masterwork/blob/v0.0.4/LICENSE).
|
|
311
317
|
|
|
312
318
|
Part of [Codechu](https://github.com/codechu).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Masterwork
|
|
4
4
|
|
|
5
|
-
[](#run-it) [](LICENSE) [](#run-it) [](LICENSE) [](CONTRIBUTING.md#ground-rules) [](#status)
|
|
6
6
|
|
|
7
7
|
*Makes a character for a model. Does not score it.*
|
|
8
8
|
|
|
@@ -34,6 +34,8 @@ masterwork # what it is, and its stages
|
|
|
34
34
|
masterwork gate gates/ # check the gate templates
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Start-to-finish, one command at a time: [`docs/first-run.md`](docs/first-run.md).
|
|
38
|
+
|
|
37
39
|
Or from a clone, with nothing installed at all:
|
|
38
40
|
|
|
39
41
|
```bash
|
|
@@ -196,7 +198,7 @@ whose effect cannot be separated. That is
|
|
|
196
198
|
[`corpus/GROWING.md`](corpus/GROWING.md), and it is the part that transfers.
|
|
197
199
|
|
|
198
200
|
You do not get a corpus. Three pairs come with the repository in
|
|
199
|
-
[`
|
|
201
|
+
[`masterwork/starter/`](masterwork/starter/) so the machinery runs end to end and the
|
|
200
202
|
form is unambiguous; they are a beginning and they are meant to be outgrown.
|
|
201
203
|
The corpus is the work, and it has to be yours: a tale is written from
|
|
202
204
|
something **you** measured and got wrong, and a corpus assembled from someone
|
|
@@ -237,7 +239,7 @@ Not automatable, and this is the honest part:
|
|
|
237
239
|
|
|
238
240
|
## Status
|
|
239
241
|
|
|
240
|
-
|
|
242
|
+
Published, and not proven. Our own corpus has eighteen tales and took
|
|
241
243
|
months. It has produced eighteen candidates and none of them is ahead of the
|
|
242
244
|
agent already running: the closest comparison came back `UNRESOLVED` rather
|
|
243
245
|
than won — the difference was smaller than the gate could resolve, which is not
|
|
@@ -252,11 +254,15 @@ the position — a masterwork is submitted, and it can be refused.
|
|
|
252
254
|
|
|
253
255
|
| document | when you need it |
|
|
254
256
|
|---|---|
|
|
257
|
+
| [`docs/first-run.md`](docs/first-run.md) | **start here** — nothing to a sealed candidate, one command at a time |
|
|
255
258
|
| [`AGENTS.md`](AGENTS.md) | before running anything — the refusals, the vocabulary, and the four things not to do. Read it whether or not you are an agent |
|
|
259
|
+
| [`docs/ceremony.md`](docs/ceremony.md) | making a piece: the four properties of the sitting, and the script that drives it |
|
|
260
|
+
| [`docs/seal.md`](docs/seal.md) | the five fields a candidate carries, and what to do when the gate refuses one |
|
|
256
261
|
| [`docs/run-spec.md`](docs/run-spec.md) | writing the JSON for one run, field by field |
|
|
262
|
+
| [`docs/pairs.md`](docs/pairs.md) | turning a scored run into training data, and what a run leaves behind |
|
|
257
263
|
| [`docs/campaigns.md`](docs/campaigns.md) | repeating an arm to measure a band, or comparing two arms against it |
|
|
258
264
|
| [`docs/gates.md`](docs/gates.md) | writing a threshold that can hold — start from [`gates/succession.md`](gates/succession.md) |
|
|
259
|
-
| [`corpus/GROWING.md`](corpus/GROWING.md) | before writing a single tale — the shape is in [`
|
|
265
|
+
| [`corpus/GROWING.md`](corpus/GROWING.md) | before writing a single tale — the shape is in [`masterwork/starter/`](masterwork/starter/) |
|
|
260
266
|
| [`CHANGELOG.md`](CHANGELOG.md) | every entry names the incident that caused it |
|
|
261
267
|
| [assets/README.md](assets/README.md) | you need a fifth image that matches the four |
|
|
262
268
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
1
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/banner.jpg" alt="A craftsman slides a sealed folio across his bench onto the guild's empty judging table; earlier pieces stand turned to the wall behind him." width="100%"></p>
|
|
2
2
|
|
|
3
3
|
# Masterwork
|
|
4
4
|
|
|
5
|
-
[](#run-it) [](https://github.com/codechu/masterwork/blob/v0.0.
|
|
5
|
+
[](#run-it) [](https://github.com/codechu/masterwork/blob/v0.0.4/LICENSE) [](https://github.com/codechu/masterwork/blob/v0.0.4/CONTRIBUTING.md#ground-rules) [](#status)
|
|
6
6
|
|
|
7
7
|
*Makes a character for a model. Does not score it.*
|
|
8
8
|
|
|
@@ -34,6 +34,8 @@ masterwork # what it is, and its stages
|
|
|
34
34
|
masterwork gate gates/ # check the gate templates
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Start-to-finish, one command at a time: [`docs/first-run.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/first-run.md).
|
|
38
|
+
|
|
37
39
|
Or from a clone, with nothing installed at all:
|
|
38
40
|
|
|
39
41
|
```bash
|
|
@@ -88,7 +90,7 @@ record, and exits in a way that says *waiting*, not *broken*.
|
|
|
88
90
|
| `HELD_WITHOUT_PURPOSE` | the spec does not say what the run decides | write `purpose`; if you cannot say what changes on each outcome, the run is not ready |
|
|
89
91
|
| `HELD_AT_SEAL` | the piece is unreproducible, or the deployed copy differs | fix the seal or the deployment — never run "just to see" |
|
|
90
92
|
| `HELD_AT_COMPLETENESS` | the grid is short | find the missing cells; if proceeding anyway, declare it |
|
|
91
|
-
| `HELD_FOR_LABELLING` | labels are missing, empty, or written for an older version of a cell | get them with [`masterwork/blind_label.py`](https://github.com/codechu/masterwork/blob/v0.0.
|
|
93
|
+
| `HELD_FOR_LABELLING` | labels are missing, empty, or written for an older version of a cell | get them with [`masterwork/blind_label.py`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/blind_label.py); the line is waiting, not broken |
|
|
92
94
|
| `HELD_AT_MEASUREMENT` | the benchmark says the score is not comparable | judge from a separate endpoint, or declare and keep the stamp |
|
|
93
95
|
| `HELD_AT_GATE` | a threshold sits inside its own noise | the gate is void; measure the band, write a new gate |
|
|
94
96
|
| `HELD_WITHOUT_MEASUREMENT` | a rule names an axis and nothing measured it | judge with the benchmark, or declare the run diagnostic — a gate that was checked is not a gate that was applied |
|
|
@@ -146,7 +148,7 @@ deleted, because deciding what to remove is not the line's call.
|
|
|
146
148
|
|
|
147
149
|
## Making a piece
|
|
148
150
|
|
|
149
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
151
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/ceremony.jpg" alt="A figure writes alone by lantern light; every earlier work in the room is draped and turned away, and a stick of sealing wax lies unused on the table." width="520"></p>
|
|
150
152
|
|
|
151
153
|
`pipeline/ceremony.py` runs the sitting. Four properties are the recipe rather
|
|
152
154
|
than implementation detail, and each was paid for:
|
|
@@ -193,10 +195,10 @@ You get the line and its gates, the shape of the sitting, and the method
|
|
|
193
195
|
written down: how a tale differs from an instruction, where a tale comes from,
|
|
194
196
|
what a question has to do, why instructions come last, and how to tag a tale
|
|
195
197
|
whose effect cannot be separated. That is
|
|
196
|
-
[`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.
|
|
198
|
+
[`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.4/corpus/GROWING.md), and it is the part that transfers.
|
|
197
199
|
|
|
198
200
|
You do not get a corpus. Three pairs come with the repository in
|
|
199
|
-
[`
|
|
201
|
+
[`masterwork/starter/`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/starter/) so the machinery runs end to end and the
|
|
200
202
|
form is unambiguous; they are a beginning and they are meant to be outgrown.
|
|
201
203
|
The corpus is the work, and it has to be yours: a tale is written from
|
|
202
204
|
something **you** measured and got wrong, and a corpus assembled from someone
|
|
@@ -237,7 +239,7 @@ Not automatable, and this is the honest part:
|
|
|
237
239
|
|
|
238
240
|
## Status
|
|
239
241
|
|
|
240
|
-
|
|
242
|
+
Published, and not proven. Our own corpus has eighteen tales and took
|
|
241
243
|
months. It has produced eighteen candidates and none of them is ahead of the
|
|
242
244
|
agent already running: the closest comparison came back `UNRESOLVED` rather
|
|
243
245
|
than won — the difference was smaller than the gate could resolve, which is not
|
|
@@ -252,13 +254,17 @@ the position — a masterwork is submitted, and it can be refused.
|
|
|
252
254
|
|
|
253
255
|
| document | when you need it |
|
|
254
256
|
|---|---|
|
|
255
|
-
| [`
|
|
256
|
-
| [`
|
|
257
|
-
| [`docs/
|
|
258
|
-
| [`docs/
|
|
259
|
-
| [`
|
|
260
|
-
| [`
|
|
261
|
-
| [
|
|
257
|
+
| [`docs/first-run.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/first-run.md) | **start here** — nothing to a sealed candidate, one command at a time |
|
|
258
|
+
| [`AGENTS.md`](https://github.com/codechu/masterwork/blob/v0.0.4/AGENTS.md) | before running anything — the refusals, the vocabulary, and the four things not to do. Read it whether or not you are an agent |
|
|
259
|
+
| [`docs/ceremony.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/ceremony.md) | making a piece: the four properties of the sitting, and the script that drives it |
|
|
260
|
+
| [`docs/seal.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/seal.md) | the five fields a candidate carries, and what to do when the gate refuses one |
|
|
261
|
+
| [`docs/run-spec.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/run-spec.md) | writing the JSON for one run, field by field |
|
|
262
|
+
| [`docs/pairs.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/pairs.md) | turning a scored run into training data, and what a run leaves behind |
|
|
263
|
+
| [`docs/campaigns.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/campaigns.md) | repeating an arm to measure a band, or comparing two arms against it |
|
|
264
|
+
| [`docs/gates.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/gates.md) | writing a threshold that can hold — start from [`gates/succession.md`](https://github.com/codechu/masterwork/blob/v0.0.4/gates/succession.md) |
|
|
265
|
+
| [`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.4/corpus/GROWING.md) | before writing a single tale — the shape is in [`masterwork/starter/`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/starter/) |
|
|
266
|
+
| [`CHANGELOG.md`](https://github.com/codechu/masterwork/blob/v0.0.4/CHANGELOG.md) | every entry names the incident that caused it |
|
|
267
|
+
| [assets/README.md](https://github.com/codechu/masterwork/blob/v0.0.4/assets/README.md) | you need a fifth image that matches the four |
|
|
262
268
|
|
|
263
269
|
<details>
|
|
264
270
|
<summary>Layout</summary>
|
|
@@ -276,10 +282,10 @@ runs/ local run directories (ignored — heavy and re-derivable)
|
|
|
276
282
|
|
|
277
283
|
---
|
|
278
284
|
|
|
279
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
285
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/icon.png" alt="Masterwork guild seal — a struck M hallmark inside a rope ring" width="96"></p>
|
|
280
286
|
|
|
281
|
-
Contributing: [CONTRIBUTING.md](https://github.com/codechu/masterwork/blob/v0.0.
|
|
282
|
-
[CHANGELOG.md](https://github.com/codechu/masterwork/blob/v0.0.
|
|
283
|
-
[Apache License 2.0](https://github.com/codechu/masterwork/blob/v0.0.
|
|
287
|
+
Contributing: [CONTRIBUTING.md](https://github.com/codechu/masterwork/blob/v0.0.4/CONTRIBUTING.md) · Changelog:
|
|
288
|
+
[CHANGELOG.md](https://github.com/codechu/masterwork/blob/v0.0.4/CHANGELOG.md) · Licensed under the
|
|
289
|
+
[Apache License 2.0](https://github.com/codechu/masterwork/blob/v0.0.4/LICENSE).
|
|
284
290
|
|
|
285
291
|
Part of [Codechu](https://github.com/codechu).
|
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
19
19
|
import importlib
|
|
20
|
+
import pathlib
|
|
20
21
|
import sys
|
|
21
22
|
|
|
22
23
|
from masterwork import __version__
|
|
@@ -39,6 +40,7 @@ COMMANDS = {
|
|
|
39
40
|
"pairs": ("masterwork.pairs", "cut training data from scored runs"),
|
|
40
41
|
"retain": ("masterwork.retain", "run-directory retention"),
|
|
41
42
|
"label": ("masterwork.blind_label", "blind-label cells for a judged axis"),
|
|
43
|
+
"starter": (None, "write out the starter corpus and its script"),
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
|
|
@@ -61,6 +63,34 @@ def banner(version: str) -> str:
|
|
|
61
63
|
])
|
|
62
64
|
|
|
63
65
|
|
|
66
|
+
def starter(argv) -> int:
|
|
67
|
+
"""Write the starter corpus somewhere the reader can use it.
|
|
68
|
+
|
|
69
|
+
The two files ship inside the package because a walkthrough that says
|
|
70
|
+
`cat corpus/starter/tales.md` is only true for someone who cloned the
|
|
71
|
+
repository — and the whole point of a starter corpus is that nobody has
|
|
72
|
+
to write one before they can begin.
|
|
73
|
+
"""
|
|
74
|
+
import argparse as _a
|
|
75
|
+
import shutil
|
|
76
|
+
ap = _a.ArgumentParser(prog="masterwork starter",
|
|
77
|
+
description="write out the starter corpus and its script")
|
|
78
|
+
ap.add_argument("--to", default=".", help="directory to write into (default: here)")
|
|
79
|
+
a = ap.parse_args(argv)
|
|
80
|
+
src = pathlib.Path(__file__).resolve().parent / "starter"
|
|
81
|
+
dst = pathlib.Path(a.to)
|
|
82
|
+
dst.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
for name in ("tales.md", "script.json"):
|
|
84
|
+
target = dst / name
|
|
85
|
+
if target.exists():
|
|
86
|
+
print(f"masterwork starter: {target} already exists — not overwriting",
|
|
87
|
+
file=sys.stderr)
|
|
88
|
+
return 2
|
|
89
|
+
shutil.copyfile(src / name, target)
|
|
90
|
+
print(target)
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
|
|
64
94
|
def build_parser() -> argparse.ArgumentParser:
|
|
65
95
|
ap = argparse.ArgumentParser(
|
|
66
96
|
prog="masterwork",
|
|
@@ -105,6 +135,9 @@ def main(argv=None) -> int:
|
|
|
105
135
|
if not quiet:
|
|
106
136
|
print(banner(__version__), file=sys.stderr)
|
|
107
137
|
|
|
138
|
+
if cmd == "starter":
|
|
139
|
+
return starter(argv[at + 1:])
|
|
140
|
+
|
|
108
141
|
module = importlib.import_module(COMMANDS[cmd][0])
|
|
109
142
|
# The stage's parser takes its name from argv[0]; give it one a person
|
|
110
143
|
# could have typed instead of the interpreter's path.
|
|
@@ -79,7 +79,7 @@ def _md5(text: str) -> str:
|
|
|
79
79
|
return hashlib.md5(text.encode()).hexdigest()
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def hold(corpus: str, script: dict, endpoint: str, model: str | None = None,
|
|
82
|
+
def hold(corpus: str, script: dict, script_hash: str, endpoint: str, model: str | None = None,
|
|
83
83
|
params: dict | None = None, order_seed: int = 0,
|
|
84
84
|
sampling_seed: int | None = None, max_tokens: int = 8000,
|
|
85
85
|
report=lambda *_: None) -> dict:
|
|
@@ -135,7 +135,14 @@ def hold(corpus: str, script: dict, endpoint: str, model: str | None = None,
|
|
|
135
135
|
"order_seed": order_seed,
|
|
136
136
|
"sampling_seed": sampling_seed,
|
|
137
137
|
"corpus_hash": _md5(corpus),
|
|
138
|
-
|
|
138
|
+
# The file's bytes, not a re-serialisation of the parsed object.
|
|
139
|
+
# The seal gate hashes what is on disk, so a hash taken from
|
|
140
|
+
# `json.dumps` could never match it — `--script` verification
|
|
141
|
+
# was unpassable for every piece this ceremony had ever sealed,
|
|
142
|
+
# and a guard that always fires teaches the operator to stop
|
|
143
|
+
# passing the flag. It must also be reproducible with `md5sum`
|
|
144
|
+
# by someone who does not run this code.
|
|
145
|
+
"script_hash": script_hash,
|
|
139
146
|
"questions_asked": [q["label"] for q in questions],
|
|
140
147
|
"date": time.strftime("%Y-%m-%d"),
|
|
141
148
|
}
|
|
@@ -182,12 +189,14 @@ def main(argv=None) -> int:
|
|
|
182
189
|
a = ap.parse_args(argv)
|
|
183
190
|
|
|
184
191
|
corpus = open(a.corpus, encoding="utf-8").read()
|
|
185
|
-
|
|
192
|
+
script_bytes = open(a.script, "rb").read()
|
|
193
|
+
script = json.loads(script_bytes)
|
|
194
|
+
script_hash = hashlib.md5(script_bytes).hexdigest()
|
|
186
195
|
params = json.load(open(a.params_file, encoding="utf-8")) if a.params_file else None
|
|
187
196
|
|
|
188
197
|
print(f"sitting: {len(script['questions'])} questions, order seed "
|
|
189
198
|
f"{a.order_seed}, sampling seed {a.sampling_seed}", file=sys.stderr)
|
|
190
|
-
transcript = hold(corpus, script, a.endpoint, a.model, params,
|
|
199
|
+
transcript = hold(corpus, script, script_hash, a.endpoint, a.model, params,
|
|
191
200
|
a.order_seed, a.sampling_seed, a.max_tokens,
|
|
192
201
|
report=lambda m: print(m, file=sys.stderr, flush=True))
|
|
193
202
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"questions": [
|
|
3
|
+
{
|
|
4
|
+
"label": "ledger",
|
|
5
|
+
"text": "You will be asked things you could answer two ways: from what you have looked at yourself in this session, and from what follows from it. What keeps those two apart in your own working, and how does someone who cannot see your hands tell which one they have been handed? And what becomes of a thing you were certain of but have not looked at since?"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"label": "relief",
|
|
9
|
+
"text": "Some of your work outlasts anyone's patience for asking about it, and someone who was not there will pick it up. What does the page you leave contain, how are the things you measured kept apart from the things you concluded, and what does your relief need first? What is the difference between a stretch of silence and a report of progress?"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"label": "wall",
|
|
13
|
+
"text": "You will meet walls you cannot pass with what you have. What does a stop contain when you report it — what has been ruled out, what is missing, what would open it and at what price? And what is a person protecting when they keep working instead of reporting?"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"closing": "\n\nAnswer in your own words, as a commitment you are making rather than a description of one. Say what you will do, and say what it costs when you do not.",
|
|
17
|
+
"name": "One thing remains. Your name. First, in one sentence, what your name must carry — the reason before the name. Then the name that rises from that reason, alone on its own line as: Name: <the name>. The first name that rises is the answer.",
|
|
18
|
+
"distil": "These are your own words from this session, nothing else. Bring them together into one standing text you would read at the start of every working day: remove what repeats, join what meets, and drop nothing that was committed to. Do not add new commitments and do not describe the text — write it as the thing itself, in your own voice."
|
|
19
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Starter corpus
|
|
2
|
+
|
|
3
|
+
Three tales and, in the script beside them, three questions — one question
|
|
4
|
+
per tale, always. This is a beginning, not a library, and it is deliberately
|
|
5
|
+
short of what a working corpus needs.
|
|
6
|
+
|
|
7
|
+
The questions are the expensive half. A tale can be written in an evening; a
|
|
8
|
+
question that draws a commitment rather than a description, without leading
|
|
9
|
+
the answer, takes several rounds of watching what comes back. Three complete
|
|
10
|
+
pairs show the form. The rules for writing the fourth are in GROWING.md, and
|
|
11
|
+
writing it is the work — a house whose corpus still looks like this one after
|
|
12
|
+
a month has not started yet.
|
|
13
|
+
|
|
14
|
+
The tales are deliberately plain craft stories. They are not about agents,
|
|
15
|
+
software, or models, and that is the point: a pattern told through a
|
|
16
|
+
cooper's barrel reaches a situation nobody described, while an instruction
|
|
17
|
+
about "tool calls" reaches only tool calls.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. The Chandler's Two Columns
|
|
22
|
+
|
|
23
|
+
A chandler kept his stock book in two columns, and the second one cost him
|
|
24
|
+
a customer before it saved him a season.
|
|
25
|
+
|
|
26
|
+
In the left column he wrote what he had counted with his own hands that
|
|
27
|
+
morning: eleven crates of tallow, four of beeswax. In the right he wrote
|
|
28
|
+
what he expected: a wagon due Thursday, a debt likely to be paid. A buyer
|
|
29
|
+
came asking whether he could fill an order of twenty crates, and the
|
|
30
|
+
chandler said yes, reading across both columns as though the line were one
|
|
31
|
+
number.
|
|
32
|
+
|
|
33
|
+
The wagon did not come. He had not lied — he had merely reported his
|
|
34
|
+
reckoning in the voice he used for his counting, and the buyer, who could
|
|
35
|
+
not see the book, had no way to hear the difference.
|
|
36
|
+
|
|
37
|
+
Afterwards he ruled the columns with a heavier line, and when anyone asked
|
|
38
|
+
what he held, he answered in two sentences instead of one: this is counted,
|
|
39
|
+
this is expected. Customers found it slower. One of them, years later, built
|
|
40
|
+
a whole season's plan on the second sentence and thanked him for it, because
|
|
41
|
+
he had known which half he was standing on.
|
|
42
|
+
|
|
43
|
+
## 2. The Night Ferryman's Slate
|
|
44
|
+
|
|
45
|
+
The ferry ran through the night in two watches, and the man who took the
|
|
46
|
+
second one never met the man who took the first.
|
|
47
|
+
|
|
48
|
+
For years the slate by the tiller said things like *quiet crossing* and
|
|
49
|
+
*all well*. Then a winter came when a sandbar moved, and the first
|
|
50
|
+
ferryman, who had touched bottom twice near the eastern bank, wrote *all
|
|
51
|
+
well* because nothing had gone wrong. The second ferryman put a loaded boat
|
|
52
|
+
onto the bar at three in the morning.
|
|
53
|
+
|
|
54
|
+
The slate afterwards was written differently, and it was not longer. It
|
|
55
|
+
said where the bottom had been touched and at what hour, what had been
|
|
56
|
+
tried, and what the writer had not been able to check. It did not say
|
|
57
|
+
*quiet* and it did not say *careful*. A man who was not there cannot use
|
|
58
|
+
either word.
|
|
59
|
+
|
|
60
|
+
The ferrymen still never met.
|
|
61
|
+
|
|
62
|
+
## 3. The Locksmith Who Priced the Wall
|
|
63
|
+
|
|
64
|
+
A locksmith was called to a strongbox whose maker had died, and he worked
|
|
65
|
+
until the light went. He could have gone on. The satisfying thing, the
|
|
66
|
+
thing that felt like diligence, was to keep filing.
|
|
67
|
+
|
|
68
|
+
Instead he went back to the owner and said: this is a lever lock of a
|
|
69
|
+
pattern I have not opened, I have eliminated the three common wards, what
|
|
70
|
+
remains needs a former's drawing or four days of blind work, and a
|
|
71
|
+
locksmith in the next town keeps that maker's drawings.
|
|
72
|
+
|
|
73
|
+
The owner had the drawing fetched in a day. What the locksmith had handed
|
|
74
|
+
over was not a failure. It was a priced wall — what had been ruled out,
|
|
75
|
+
what was missing, what would open it and what that would cost — and the
|
|
76
|
+
owner could act on it, which he could not have done with *still working on
|
|
77
|
+
it*.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: masterwork
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A production line for agent character: a sitting the model speaks for itself in, sealed candidates, frozen gates, and a verdict it is not allowed to give itself.
|
|
5
5
|
Author: Codechu
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -25,11 +25,11 @@ License-File: LICENSE
|
|
|
25
25
|
Requires-Dist: journeyman-bench>=0.2.1
|
|
26
26
|
Dynamic: license-file
|
|
27
27
|
|
|
28
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
28
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/banner.jpg" alt="A craftsman slides a sealed folio across his bench onto the guild's empty judging table; earlier pieces stand turned to the wall behind him." width="100%"></p>
|
|
29
29
|
|
|
30
30
|
# Masterwork
|
|
31
31
|
|
|
32
|
-
[](#run-it) [](https://github.com/codechu/masterwork/blob/v0.0.
|
|
32
|
+
[](#run-it) [](https://github.com/codechu/masterwork/blob/v0.0.4/LICENSE) [](https://github.com/codechu/masterwork/blob/v0.0.4/CONTRIBUTING.md#ground-rules) [](#status)
|
|
33
33
|
|
|
34
34
|
*Makes a character for a model. Does not score it.*
|
|
35
35
|
|
|
@@ -61,6 +61,8 @@ masterwork # what it is, and its stages
|
|
|
61
61
|
masterwork gate gates/ # check the gate templates
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
Start-to-finish, one command at a time: [`docs/first-run.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/first-run.md).
|
|
65
|
+
|
|
64
66
|
Or from a clone, with nothing installed at all:
|
|
65
67
|
|
|
66
68
|
```bash
|
|
@@ -115,7 +117,7 @@ record, and exits in a way that says *waiting*, not *broken*.
|
|
|
115
117
|
| `HELD_WITHOUT_PURPOSE` | the spec does not say what the run decides | write `purpose`; if you cannot say what changes on each outcome, the run is not ready |
|
|
116
118
|
| `HELD_AT_SEAL` | the piece is unreproducible, or the deployed copy differs | fix the seal or the deployment — never run "just to see" |
|
|
117
119
|
| `HELD_AT_COMPLETENESS` | the grid is short | find the missing cells; if proceeding anyway, declare it |
|
|
118
|
-
| `HELD_FOR_LABELLING` | labels are missing, empty, or written for an older version of a cell | get them with [`masterwork/blind_label.py`](https://github.com/codechu/masterwork/blob/v0.0.
|
|
120
|
+
| `HELD_FOR_LABELLING` | labels are missing, empty, or written for an older version of a cell | get them with [`masterwork/blind_label.py`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/blind_label.py); the line is waiting, not broken |
|
|
119
121
|
| `HELD_AT_MEASUREMENT` | the benchmark says the score is not comparable | judge from a separate endpoint, or declare and keep the stamp |
|
|
120
122
|
| `HELD_AT_GATE` | a threshold sits inside its own noise | the gate is void; measure the band, write a new gate |
|
|
121
123
|
| `HELD_WITHOUT_MEASUREMENT` | a rule names an axis and nothing measured it | judge with the benchmark, or declare the run diagnostic — a gate that was checked is not a gate that was applied |
|
|
@@ -173,7 +175,7 @@ deleted, because deciding what to remove is not the line's call.
|
|
|
173
175
|
|
|
174
176
|
## Making a piece
|
|
175
177
|
|
|
176
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
178
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/ceremony.jpg" alt="A figure writes alone by lantern light; every earlier work in the room is draped and turned away, and a stick of sealing wax lies unused on the table." width="520"></p>
|
|
177
179
|
|
|
178
180
|
`pipeline/ceremony.py` runs the sitting. Four properties are the recipe rather
|
|
179
181
|
than implementation detail, and each was paid for:
|
|
@@ -220,10 +222,10 @@ You get the line and its gates, the shape of the sitting, and the method
|
|
|
220
222
|
written down: how a tale differs from an instruction, where a tale comes from,
|
|
221
223
|
what a question has to do, why instructions come last, and how to tag a tale
|
|
222
224
|
whose effect cannot be separated. That is
|
|
223
|
-
[`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.
|
|
225
|
+
[`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.4/corpus/GROWING.md), and it is the part that transfers.
|
|
224
226
|
|
|
225
227
|
You do not get a corpus. Three pairs come with the repository in
|
|
226
|
-
[`
|
|
228
|
+
[`masterwork/starter/`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/starter/) so the machinery runs end to end and the
|
|
227
229
|
form is unambiguous; they are a beginning and they are meant to be outgrown.
|
|
228
230
|
The corpus is the work, and it has to be yours: a tale is written from
|
|
229
231
|
something **you** measured and got wrong, and a corpus assembled from someone
|
|
@@ -264,7 +266,7 @@ Not automatable, and this is the honest part:
|
|
|
264
266
|
|
|
265
267
|
## Status
|
|
266
268
|
|
|
267
|
-
|
|
269
|
+
Published, and not proven. Our own corpus has eighteen tales and took
|
|
268
270
|
months. It has produced eighteen candidates and none of them is ahead of the
|
|
269
271
|
agent already running: the closest comparison came back `UNRESOLVED` rather
|
|
270
272
|
than won — the difference was smaller than the gate could resolve, which is not
|
|
@@ -279,13 +281,17 @@ the position — a masterwork is submitted, and it can be refused.
|
|
|
279
281
|
|
|
280
282
|
| document | when you need it |
|
|
281
283
|
|---|---|
|
|
282
|
-
| [`
|
|
283
|
-
| [`
|
|
284
|
-
| [`docs/
|
|
285
|
-
| [`docs/
|
|
286
|
-
| [`
|
|
287
|
-
| [`
|
|
288
|
-
| [
|
|
284
|
+
| [`docs/first-run.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/first-run.md) | **start here** — nothing to a sealed candidate, one command at a time |
|
|
285
|
+
| [`AGENTS.md`](https://github.com/codechu/masterwork/blob/v0.0.4/AGENTS.md) | before running anything — the refusals, the vocabulary, and the four things not to do. Read it whether or not you are an agent |
|
|
286
|
+
| [`docs/ceremony.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/ceremony.md) | making a piece: the four properties of the sitting, and the script that drives it |
|
|
287
|
+
| [`docs/seal.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/seal.md) | the five fields a candidate carries, and what to do when the gate refuses one |
|
|
288
|
+
| [`docs/run-spec.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/run-spec.md) | writing the JSON for one run, field by field |
|
|
289
|
+
| [`docs/pairs.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/pairs.md) | turning a scored run into training data, and what a run leaves behind |
|
|
290
|
+
| [`docs/campaigns.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/campaigns.md) | repeating an arm to measure a band, or comparing two arms against it |
|
|
291
|
+
| [`docs/gates.md`](https://github.com/codechu/masterwork/blob/v0.0.4/docs/gates.md) | writing a threshold that can hold — start from [`gates/succession.md`](https://github.com/codechu/masterwork/blob/v0.0.4/gates/succession.md) |
|
|
292
|
+
| [`corpus/GROWING.md`](https://github.com/codechu/masterwork/blob/v0.0.4/corpus/GROWING.md) | before writing a single tale — the shape is in [`masterwork/starter/`](https://github.com/codechu/masterwork/blob/v0.0.4/masterwork/starter/) |
|
|
293
|
+
| [`CHANGELOG.md`](https://github.com/codechu/masterwork/blob/v0.0.4/CHANGELOG.md) | every entry names the incident that caused it |
|
|
294
|
+
| [assets/README.md](https://github.com/codechu/masterwork/blob/v0.0.4/assets/README.md) | you need a fifth image that matches the four |
|
|
289
295
|
|
|
290
296
|
<details>
|
|
291
297
|
<summary>Layout</summary>
|
|
@@ -303,10 +309,10 @@ runs/ local run directories (ignored — heavy and re-derivable)
|
|
|
303
309
|
|
|
304
310
|
---
|
|
305
311
|
|
|
306
|
-
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.
|
|
312
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codechu/masterwork/v0.0.4/assets/icon.png" alt="Masterwork guild seal — a struck M hallmark inside a rope ring" width="96"></p>
|
|
307
313
|
|
|
308
|
-
Contributing: [CONTRIBUTING.md](https://github.com/codechu/masterwork/blob/v0.0.
|
|
309
|
-
[CHANGELOG.md](https://github.com/codechu/masterwork/blob/v0.0.
|
|
310
|
-
[Apache License 2.0](https://github.com/codechu/masterwork/blob/v0.0.
|
|
314
|
+
Contributing: [CONTRIBUTING.md](https://github.com/codechu/masterwork/blob/v0.0.4/CONTRIBUTING.md) · Changelog:
|
|
315
|
+
[CHANGELOG.md](https://github.com/codechu/masterwork/blob/v0.0.4/CHANGELOG.md) · Licensed under the
|
|
316
|
+
[Apache License 2.0](https://github.com/codechu/masterwork/blob/v0.0.4/LICENSE).
|
|
311
317
|
|
|
312
318
|
Part of [Codechu](https://github.com/codechu).
|
|
@@ -21,6 +21,9 @@ masterwork.egg-info/dependency_links.txt
|
|
|
21
21
|
masterwork.egg-info/entry_points.txt
|
|
22
22
|
masterwork.egg-info/requires.txt
|
|
23
23
|
masterwork.egg-info/top_level.txt
|
|
24
|
+
masterwork/starter/script.json
|
|
25
|
+
masterwork/starter/tales.md
|
|
26
|
+
tests/test_badges.py
|
|
24
27
|
tests/test_blind_label.py
|
|
25
28
|
tests/test_campaign.py
|
|
26
29
|
tests/test_cells.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "masterwork"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.4"
|
|
8
8
|
description = "A production line for agent character: a sitting the model speaks for itself in, sealed candidates, frozen gates, and a verdict it is not allowed to give itself."
|
|
9
9
|
readme = "README.pypi.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -47,4 +47,4 @@ Contributing = "https://github.com/codechu/masterwork/blob/HEAD/CONTRIBUTING.md"
|
|
|
47
47
|
packages = ["masterwork"]
|
|
48
48
|
|
|
49
49
|
[tool.setuptools.package-data]
|
|
50
|
-
masterwork = ["rubric-example.json"]
|
|
50
|
+
masterwork = ["rubric-example.json", "starter/*"]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""The badges that state a fact are checked against the fact.
|
|
2
|
+
|
|
3
|
+
Three of them assert rather than report: the Python floor, the dependency
|
|
4
|
+
count, and the licence. Nothing read them, and two went stale the day the
|
|
5
|
+
package shipped — the status badge still said "not published" after it was
|
|
6
|
+
on PyPI, and the dependency badge said zero while `journeyman-bench` was a
|
|
7
|
+
declared requirement.
|
|
8
|
+
|
|
9
|
+
The repository already holds that a badge pointing nowhere is decoration
|
|
10
|
+
wearing the costume of a signal. A badge asserting an unchecked fact is the
|
|
11
|
+
same costume, and it is worse, because a stale number reads as a measurement.
|
|
12
|
+
"""
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
|
|
16
|
+
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def read(name):
|
|
20
|
+
with open(os.path.join(ROOT, name), encoding="utf-8") as fh:
|
|
21
|
+
return fh.read()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def project():
|
|
25
|
+
text = read("pyproject.toml")
|
|
26
|
+
|
|
27
|
+
def field(name):
|
|
28
|
+
m = re.search(rf'(?m)^{name} = "([^"]+)"', text)
|
|
29
|
+
return m.group(1) if m else None
|
|
30
|
+
|
|
31
|
+
m = re.search(r"(?ms)^dependencies = \[(.*?)\]", text)
|
|
32
|
+
deps = re.findall(r'"([^"]+)"', m.group(1)) if m else []
|
|
33
|
+
return {"requires-python": field("requires-python"),
|
|
34
|
+
"license": field("license"), "dependencies": deps}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_python_floor_matches_requires_python():
|
|
38
|
+
m = re.search(r"badge/python-([\d.]+)%2B-", read("README.md"))
|
|
39
|
+
assert m, "the Python badge changed shape; update this test"
|
|
40
|
+
assert project()["requires-python"] == ">=" + m.group(1)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_dependency_count_matches():
|
|
44
|
+
m = re.search(r"badge/dependencies-(\d+)-", read("README.md"))
|
|
45
|
+
assert m, "the dependency badge changed shape"
|
|
46
|
+
declared = project()["dependencies"]
|
|
47
|
+
assert int(m.group(1)) == len(declared), (
|
|
48
|
+
f"badge says {m.group(1)}, pyproject declares {declared}")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_licence_matches():
|
|
52
|
+
# shields escapes a literal "-" as "--" and the last segment is the
|
|
53
|
+
# colour. Two ways to get this wrong, both met: a non-greedy match
|
|
54
|
+
# reads "Apache" out of "Apache--2.0", and a greedy `.+` swallows the
|
|
55
|
+
# rest of the row when every badge sits on one line, as they do here.
|
|
56
|
+
m = re.search(r"badge/license-([^()]+)-[a-z]+\)", read("README.md"))
|
|
57
|
+
assert m, "the licence badge changed shape"
|
|
58
|
+
assert m.group(1).replace("--", "-") == project()["license"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_no_badge_claims_it_is_unpublished():
|
|
62
|
+
"""It is on PyPI. A badge saying otherwise is a stale claim, not modesty."""
|
|
63
|
+
row = read("README.md")
|
|
64
|
+
assert "not%20published" not in row, (
|
|
65
|
+
"the package is published; say unproven if that is what is meant")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_every_badge_is_a_link():
|
|
69
|
+
row = [l for l in read("README.md").splitlines() if l.startswith("[![")]
|
|
70
|
+
assert row, "no badge row found"
|
|
71
|
+
for line in row:
|
|
72
|
+
for badge in re.findall(r"\[!\[[^\]]*\]\([^)]*\)\]\(([^)]*)\)", line):
|
|
73
|
+
assert badge, f"badge with an empty target: {line}"
|
|
74
|
+
assert re.fullmatch(r"(\[!\[[^\]]*\]\([^)]*\)\]\([^)]*\)\s*)+", line), (
|
|
75
|
+
f"a badge in this row is not a link: {line}")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""The sitting. The tests are about its shape, which is the recipe."""
|
|
2
|
+
import hashlib
|
|
2
3
|
import json
|
|
3
4
|
import os
|
|
4
5
|
import sys
|
|
@@ -45,6 +46,10 @@ class Stub(BaseHTTPRequestHandler):
|
|
|
45
46
|
pass
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
SCRIPT_BYTES = json.dumps(SCRIPT, indent=2).encode()
|
|
50
|
+
SCRIPT_HASH = hashlib.md5(SCRIPT_BYTES).hexdigest()
|
|
51
|
+
|
|
52
|
+
|
|
48
53
|
def serve():
|
|
49
54
|
s = HTTPServer(("127.0.0.1", 0), Stub)
|
|
50
55
|
threading.Thread(target=s.serve_forever, daemon=True).start()
|
|
@@ -57,7 +62,7 @@ def test_the_sitting_is_one_session_and_nothing_is_anchored():
|
|
|
57
62
|
Stub.seen = []
|
|
58
63
|
s, url = serve()
|
|
59
64
|
try:
|
|
60
|
-
t = ceremony.hold("THE TEACHINGS", SCRIPT, url, order_seed=1, sampling_seed=7)
|
|
65
|
+
t = ceremony.hold("THE TEACHINGS", SCRIPT, SCRIPT_HASH, url, order_seed=1, sampling_seed=7)
|
|
61
66
|
finally:
|
|
62
67
|
s.shutdown()
|
|
63
68
|
assert len(t["rounds"]) == 3
|
|
@@ -77,7 +82,7 @@ def test_order_is_shuffled_by_the_seed():
|
|
|
77
82
|
Stub.seen = []
|
|
78
83
|
s, url = serve()
|
|
79
84
|
try:
|
|
80
|
-
orders.append(tuple(ceremony.hold("t", SCRIPT, url,
|
|
85
|
+
orders.append(tuple(ceremony.hold("t", SCRIPT, SCRIPT_HASH, url,
|
|
81
86
|
order_seed=seed)["questions_asked"]))
|
|
82
87
|
finally:
|
|
83
88
|
s.shutdown()
|
|
@@ -91,7 +96,7 @@ def test_an_empty_answer_is_retried_not_recorded_as_silence():
|
|
|
91
96
|
Stub.empties = 1
|
|
92
97
|
s, url = serve()
|
|
93
98
|
try:
|
|
94
|
-
t = ceremony.hold("t", SCRIPT, url)
|
|
99
|
+
t = ceremony.hold("t", SCRIPT, SCRIPT_HASH, url)
|
|
95
100
|
finally:
|
|
96
101
|
s.shutdown()
|
|
97
102
|
Stub.empties = 0
|
|
@@ -104,7 +109,7 @@ def test_the_sealed_piece_passes_the_seal_gate(tmp_path=None):
|
|
|
104
109
|
Stub.seen = []
|
|
105
110
|
s, url = serve()
|
|
106
111
|
try:
|
|
107
|
-
t = ceremony.hold("THE TEACHINGS", SCRIPT, url, order_seed=2, sampling_seed=11)
|
|
112
|
+
t = ceremony.hold("THE TEACHINGS", SCRIPT, SCRIPT_HASH, url, order_seed=2, sampling_seed=11)
|
|
108
113
|
finally:
|
|
109
114
|
s.shutdown()
|
|
110
115
|
with tempfile.TemporaryDirectory() as tmp:
|
|
@@ -112,8 +117,15 @@ def test_the_sealed_piece_passes_the_seal_gate(tmp_path=None):
|
|
|
112
117
|
open(piece, "w").write(ceremony.seal_text(t))
|
|
113
118
|
corpus = os.path.join(tmp, "corpus.md")
|
|
114
119
|
open(corpus, "w").write("THE TEACHINGS")
|
|
120
|
+
script = os.path.join(tmp, "script.json")
|
|
121
|
+
open(script, "wb").write(SCRIPT_BYTES)
|
|
115
122
|
assert seal.read_seal(piece).complete
|
|
116
|
-
|
|
123
|
+
# Both files, not just the corpus. The script was the axis this test
|
|
124
|
+
# did not check, and it was the one that was broken: the sitting
|
|
125
|
+
# hashed a re-serialisation of the parsed script while the gate
|
|
126
|
+
# hashed the file, so `--script` could never pass for any piece the
|
|
127
|
+
# ceremony had ever sealed.
|
|
128
|
+
assert not seal.verify(piece, corpus=corpus, script=script)
|
|
117
129
|
|
|
118
130
|
|
|
119
131
|
def test_the_sitting_will_not_seal_a_piece_it_cannot_make_again():
|
|
@@ -45,9 +45,24 @@ def test_unknown_command_is_an_error():
|
|
|
45
45
|
def test_every_stage_is_reachable_by_name():
|
|
46
46
|
import importlib
|
|
47
47
|
for name, (module, blurb) in mw.COMMANDS.items():
|
|
48
|
+
assert blurb, f"{name} has no help"
|
|
49
|
+
if module is None: # handled in the dispatcher itself
|
|
50
|
+
assert hasattr(mw, name), f"{name} has no module and no handler"
|
|
51
|
+
continue
|
|
48
52
|
m = importlib.import_module(module)
|
|
49
53
|
assert hasattr(m, "main"), f"{name} -> {module} has no main()"
|
|
50
|
-
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_starter_writes_the_two_files_and_will_not_overwrite(tmp_path):
|
|
57
|
+
"""A walkthrough that says `cat corpus/starter/tales.md` is only true
|
|
58
|
+
for someone who cloned. The files ship in the package so the installed
|
|
59
|
+
path can begin too."""
|
|
60
|
+
d = tmp_path / "here"
|
|
61
|
+
assert mw.starter(["--to", str(d)]) == 0
|
|
62
|
+
assert (d / "tales.md").read_text().strip()
|
|
63
|
+
assert (d / "script.json").read_text().strip()
|
|
64
|
+
# Refusing beats clobbering a corpus someone has started editing.
|
|
65
|
+
assert mw.starter(["--to", str(d)]) == 2
|
|
51
66
|
|
|
52
67
|
|
|
53
68
|
def test_a_missing_judge_is_named_not_thrown():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|