workproof 0.3.1 → 0.4.0

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.
package/README.md CHANGED
@@ -229,6 +229,16 @@ Three commands, in increasing order of what the reader needs to have.
229
229
  compares HEAD, then recomputes every figure and prints what differs. A report from another
230
230
  repository stops at the fingerprint. If HEAD moved since the report, it says so and shows
231
231
  which figures changed.
232
+ The hash is reproducible **across operating systems, git versions, locales and directory
233
+ names**, for the same repository at the same commit with the same command and the same
234
+ `--fingerprint-key`. It is not reproducible across a rewritten history, a different sample
235
+ or a different seed, and it is not meant to be: those change the measurement. Two fields
236
+ in the report are deliberately outside the hash, because a verifier on another machine
237
+ cannot reproduce them: the local directory name, and the git version. They stay in the
238
+ document, where they explain why two runs might legitimately differ. Before 0.4.0 both
239
+ were inside it, so the same repository in a folder with a different name hashed
240
+ differently; a test now pins all of this, and CI checks it on Linux, macOS and Windows.
241
+
232
242
  - `workproof attest report.json` writes `report.intoto.json`, an
233
243
  [in-toto](https://in-toto.io) v1 Statement whose subject is the report hash and whose
234
244
  predicate carries the tool version, parameters, HEAD, keyed fingerprint, git version and
@@ -483,6 +493,28 @@ with an attestation if the reader cannot reach the repository.
483
493
  It measures survivorship and activity, not quality, review, design or mentoring. It does
484
494
  not rank people. It does not replace references. It is not a legal document.
485
495
 
496
+ ## What the research says about numbers like these
497
+
498
+ Repository-derived figures have a literature, and most of it is a warning. Kalliamvakou,
499
+ Gousios and Blincoe catalogued the traps in mining a forge, including that most repositories
500
+ are personal and inactive and that a large share of merged pull requests are not recorded as
501
+ merged ([MSR 2014](https://doi.org/10.1145/2597073.2597074), extended in [EMSE
502
+ 2015](https://doi.org/10.1007/s10664-015-9393-5)). Bird and colleagues related ownership
503
+ concentration to defects rather than to credit, which is a different question from this one
504
+ and is not evidence that a high share means good work. On survival, Spinellis, Louridas and
505
+ Kechagia put the median line lifespan at about 2.4 years across 89 repositories ([PeerJ CS
506
+ 7:e372, 2021](https://doi.org/10.7717/peerj-cs.372)), and Gurov found over half of all lines
507
+ are never deleted in 120 TypeScript repositories ([arXiv
508
+ 2606.04993](https://arxiv.org/abs/2606.04993)).
509
+
510
+ None of that work measures a person's share of the surviving code, which is what the
511
+ surviving-lines figure here reports. A first measurement of how far that share diverges from
512
+ commit share, over twelve repositories, is in
513
+ [surviving-lines/research](https://github.com/Bubblegunn/surviving-lines/tree/main/research):
514
+ the same person tops both in 25 of 36 runs, and among substantial contributors the two
515
+ differ by a median of 7.5 percentage points. That is the honest size of the effect this
516
+ report exists to expose, measured rather than asserted.
517
+
486
518
  ## Where it comes from
487
519
 
488
520
  The method is written up in
@@ -10,7 +10,6 @@ export interface Report {
10
10
  /** sha256 of the RFC 8785 canonical JSON of { params, repositories }. */
11
11
  hash: string;
12
12
  }
13
- /** The hash a report with these parameters and repositories must carry. */
14
13
  export declare const hashOf: (params: unknown, repositories: unknown) => string;
15
14
  export declare function buildReport(repositories: RepoReport[], params: Params, meta: {
16
15
  version: string;
@@ -3,7 +3,34 @@ import { canonicalize } from "./canonical.js";
3
3
  import { publicEmail } from "./analyse.js";
4
4
  import { plainSummary } from "./summary.js";
5
5
  /** The hash a report with these parameters and repositories must carry. */
6
- export const hashOf = (params, repositories) => createHash("sha256").update(canonicalize({ params, repositories })).digest("hex");
6
+ /**
7
+ * What of a repository the hash covers: everything a stranger with the same repository at
8
+ * the same commit can reproduce, and nothing else.
9
+ *
10
+ * Two fields are recorded in the report and deliberately left out. `name` is the local
11
+ * directory's basename, so the same repository cloned to a different folder hashed
12
+ * differently. `environment.git` is the local git version string, so two people with the
13
+ * same repository and different git hashed differently. Both were reproduced with real
14
+ * runs before 0.4.0. They stay in the document because they explain why two runs might
15
+ * legitimately differ; they are not something a verifier can be asked to reproduce.
16
+ *
17
+ * The blame flags, the ignore-revs file and the seed stay in, because those are the
18
+ * question the caller asked, not the machine they asked it on.
19
+ */
20
+ function forHash(repositories) {
21
+ if (!Array.isArray(repositories))
22
+ return repositories;
23
+ return repositories.map((repo) => {
24
+ if (repo === null || typeof repo !== "object")
25
+ return repo;
26
+ const { name: _name, environment, ...rest } = repo;
27
+ if (environment === null || typeof environment !== "object")
28
+ return rest;
29
+ const { git: _git, ...restEnvironment } = environment;
30
+ return { ...rest, environment: restEnvironment };
31
+ });
32
+ }
33
+ export const hashOf = (params, repositories) => createHash("sha256").update(canonicalize({ params, repositories: forHash(repositories) })).digest("hex");
7
34
  /**
8
35
  * What of the parameters the report may carry: never the fingerprint key, never a GitHub
9
36
  * noreply login, and without --emails no author given as an email address.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workproof",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Turn a private git repository into a verifiable engineering report for one author, without showing any code: thirteen figures from git, a hash anyone can recompute offline, verify, and an in-toto attestation.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",