plumb-line-provenance 0.3.0 → 0.3.1

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 (2) hide show
  1. package/package.json +5 -2
  2. package/provenance.mjs +26 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plumb-line-provenance",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Conservative provenance/confidence/lineage envelope with a taint-propagation combination law. Mock or low-confidence data cannot launder itself clean.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
@@ -28,6 +28,9 @@
28
28
  ],
29
29
  "author": "Aoife Okonedo Martin",
30
30
  "license": "Apache-2.0",
31
+ "bugs": {
32
+ "url": "https://github.com/effythealien/plumb-line/issues"
33
+ },
31
34
  "homepage": "https://github.com/effythealien/plumb-line/tree/main/primitives",
32
35
  "repository": {
33
36
  "type": "git",
@@ -38,6 +41,6 @@
38
41
  "node": ">=16"
39
42
  },
40
43
  "devDependencies": {
41
- "vitest": "^2.0.0"
44
+ "vitest": "^4.1.9"
42
45
  }
43
46
  }
package/provenance.mjs CHANGED
@@ -90,18 +90,24 @@ export function combineConfidenceScore(scores) {
90
90
  return Math.min(...scores);
91
91
  }
92
92
 
93
- let __stepCounter = 0;
94
- // Test-only: exposed so test suites can isolate step counter state between runs.
95
- // Not intended for production use; call sites should use combineProvenance/derive.
96
- export function __resetStepCounter() {
97
- __stepCounter = 0;
98
- }
99
- function nextStepId() {
100
- __stepCounter += 1;
101
- return `step-${__stepCounter}`;
102
- }
93
+ // Deprecated no-op, kept for import compatibility. Step IDs are now assigned by
94
+ // a counter local to each combineProvenance call (see below), so there is no
95
+ // shared state to reset between runs. Safe to delete from call sites.
96
+ export function __resetStepCounter() {}
103
97
 
104
98
  export function combineProvenance(...metas) {
99
+ // A value combined from no inputs is derived from nothing — honestly
100
+ // 'unavailable', not 'derived'. Returning 'derived' with an empty lineage
101
+ // would contradict auditMeta's "derived value has no lineage" check
102
+ // (SPEC §3 vs §5). See #25.
103
+ if (metas.length === 0) {
104
+ return makeMeta({
105
+ source: "unavailable",
106
+ confidence: "none",
107
+ derivedFromMock: false,
108
+ lineage: [],
109
+ });
110
+ }
105
111
  const derivedFromMock = metas.some((m) => taints(m));
106
112
  const confidence = weakestConfidence(...metas.map((m) => m?.confidence));
107
113
  const confidenceScore = combineConfidenceScore(
@@ -112,7 +118,6 @@ export function combineProvenance(...metas) {
112
118
  );
113
119
  const inputSteps = metas.map((m) => {
114
120
  const step = {
115
- id: nextStepId(),
116
121
  of: "input",
117
122
  source: m?.source,
118
123
  confidence: m?.confidence,
@@ -123,7 +128,16 @@ export function combineProvenance(...metas) {
123
128
  if (isScore(m?.confidenceScore)) step.confidenceScore = m.confidenceScore;
124
129
  return step;
125
130
  });
126
- const lineage = [...priorLineage, ...inputSteps];
131
+ // Renumber the *entire* output lineage from a combine-local counter, so step
132
+ // IDs are unique-within-output (SPEC §4) for every input shape — two
133
+ // independently-built inputs each start at step-1, so seeding past the prior
134
+ // length alone wouldn't stop their inherited steps from colliding. No
135
+ // module-level state means concurrent combines can't collide either. IDs are
136
+ // thus a pure function of output structure, not creation order. See #23.
137
+ const lineage = [...priorLineage, ...inputSteps].map((s, i) => ({
138
+ ...s,
139
+ id: `step-${i + 1}`,
140
+ }));
127
141
  return makeMeta({
128
142
  source: "derived",
129
143
  confidence,