bmad-module-skill-forge 8.1.0 → 9.0.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.
@@ -16,8 +16,13 @@ It covers the four branches §2c enumerates:
16
16
 
17
17
  * "barrel" (enumerated path) — intersect documented_set against the source
18
18
  barrel_set: Documented = |documented_set ∩ barrel_set|,
19
- Missing = barrel_set − documented_set, Stale = documented_set − barrel_set,
20
- Export Coverage = |Documented| / |barrel_set| * 100.
19
+ Missing = barrel_set − documented_set, Export Coverage = |Documented| /
20
+ |barrel_set| * 100, Stale = documented_set − (barrel_set ∪ `allSet`).
21
+ `allSet` is the whole public surface: the `all` set and every name on
22
+ the surface's export records, since a nested Python entry's names leave
23
+ `all` but keep their records. So a documented name the counted set
24
+ leaves out but the source exports (a Tier B name) is neither counted nor
25
+ stale. Without `allSet`, Stale = documented_set − barrel_set.
21
26
 
22
27
  * "scalar" (§2b's scalar branch, stats.effective_denominator): the
23
28
  denominator is effective_denominator, Missing = max(0, denominator −
@@ -34,10 +39,9 @@ It covers the four branches §2c enumerates:
34
39
  present, or `verifiedNumerator`; `missing` lists no name.
35
40
  Stale: on a surface read from the source (`sourceRead`, its
36
41
  `guards.deflation.applicable`, or a counted set), documented_set − (S ∪
37
- the whole public surface, `allSet`, the `all` set: a nested package's
38
- name is in the scope sets alone), so a documented name outside S that
39
- the source exports is neither counted nor stale; else not enumerable
40
- (empty).
42
+ `allSet`, the whole public surface, as on the barrel branch), so a
43
+ documented name outside S that the source exports is neither counted
44
+ nor stale; else not enumerable (empty).
41
45
 
42
46
  * "stack" (skill_type == "stack", empty source barrel): look each
43
47
  composition-surface name (provenance-map cited contracts, ::-excluded, or
@@ -83,9 +87,12 @@ each sets the input field it names and wins over the same field in the JSON:
83
87
  --inventory PATH exports: the validated inventory validate-inventory.py
84
88
  wrote with --output
85
89
  --surface PATH barrelSet: one name set of load-coverage-inputs.py
86
- surface, chosen by --surface-set (default "all"),
87
- with what the scalar branch reads: surfaceSet (that
88
- name), allSet (its `all` set), sourceRead (its
90
+ surface, chosen by --surface-set (default "all");
91
+ allSet, the whole surface both branches find a
92
+ stale name against (its `all` set and every name on
93
+ its `exports` records, a nested entry's included);
94
+ and what the scalar branch reads: surfaceSet (that
95
+ name), sourceRead (its
89
96
  `guards.deflation.applicable`) and setCounted (its
90
97
  `denominator.setCounted`, when --surface-set names
91
98
  its `denominator.surfaceSet`)
@@ -107,7 +114,7 @@ Input schema (one object):
107
114
  "barrelSet": ["a", "b", ...], # barrel, scalar: resolved name set
108
115
  "perFileResults": [ {"exports_found": ["a", ...]} ], # barrel: union'd if no barrelSet
109
116
  "surfaceSet": "tier_a_include", # scalar: barrelSet's name (null or "" is "all")
110
- "allSet": ["a", "b", "c", ...], # scalar: the whole public surface
117
+ "allSet": ["a", "b", "c", ...], # barrel, scalar: the whole public surface (stale)
111
118
  "sourceRead": <bool>, # scalar: the surface was read from the source
112
119
  "setCounted": <bool>, # scalar: count barrelSet (a trusted scope set)
113
120
  "denominatorValue": <int>, # scalar/stack: resolved denominator
@@ -297,6 +304,9 @@ def _validate(inp):
297
304
  if source == "barrel":
298
305
  if inp.get("barrelSet") is None and inp.get("perFileResults") is None:
299
306
  return "barrel branch requires either `barrelSet` or `perFileResults`"
307
+ all_set = inp.get("allSet")
308
+ if all_set is not None and not isinstance(all_set, list):
309
+ return "barrel branch: `allSet` must be a list of names"
300
310
  elif source == "docsOnly":
301
311
  if not isinstance(inp.get("exports"), list):
302
312
  return "docsOnly branch requires the §1 inventory `exports` (or --inventory)"
@@ -343,15 +353,28 @@ def _reads_surface(inp):
343
353
  return inp.get("barrelSet") is not None and (inp.get("sourceRead") is True or inp.get("setCounted") is True)
344
354
 
345
355
 
356
+ def _whole(inp, named):
357
+ """The whole source surface both branches find a stale name against: the counted set `named` and `allSet`
358
+ (from --surface, the `all` set and every name on the surface's export records, so a nested Python entry's
359
+ names count though they leave `all`). The counted set is always part of it.
360
+
361
+ >>> sorted(_whole({"allSet": ["alpha", "tierB", "nested", "", 3]}, {"alpha", "scoped"}))
362
+ ['alpha', 'nested', 'scoped', 'tierB']
363
+ >>> sorted(_whole({}, {"alpha"}))
364
+ ['alpha']
365
+ """
366
+ return named | {n for n in inp.get("allSet") or [] if isinstance(n, str) and n}
367
+
368
+
346
369
  def _scalar_surface(inp, denominator):
347
370
  """What the scalar branch reads from a surface read from the source: the stale names, the documented names
348
- (S ∪ `allSet`, where a nested package's name sits in the scope sets alone) lacks, and, when it counts its
349
- named set S (`setCounted`, a non-empty scope set), the documented names S holds, the names of S the skill
350
- does not document and the set's size, with the warnings. The denominator stays the stored count."""
371
+ the whole surface (`_whole`: S and `allSet`) lacks, and, when it counts its named set S (`setCounted`, a
372
+ non-empty scope set), the documented names S holds, the names of S the skill does not document and the
373
+ set's size, with the warnings. The denominator stays the stored count."""
351
374
  documented_set = build_documented_set(inp.get("exports"))
352
375
  named = {n for n in inp["barrelSet"] if isinstance(n, str) and n}
353
376
  set_name = _surface_set_name(inp)
354
- whole = named | {n for n in inp.get("allSet") or [] if isinstance(n, str) and n}
377
+ whole = _whole(inp, named)
355
378
  warnings = []
356
379
  counted = inp.get("setCounted") is True and set_name != "all"
357
380
  if counted and not named:
@@ -414,7 +437,8 @@ def reconcile(inp, doc_text=None):
414
437
  )
415
438
  documented_names = documented_set & barrel_set
416
439
  missing = sorted(barrel_set - documented_set)
417
- stale = sorted(documented_set - barrel_set)
440
+ # A documented name the counted set leaves out but the whole surface holds is neither counted nor stale.
441
+ stale = sorted(documented_set - _whole(inp, barrel_set))
418
442
  denominator = len(barrel_set)
419
443
  documented = len(documented_names)
420
444
  return {
@@ -799,11 +823,15 @@ def apply_file_inputs(inp, args):
799
823
  raise InputFileError(f"--surface {args.surface} has no name set `{args.surface_set}`")
800
824
  inp["barrelSet"] = names
801
825
  # The scalar branch counts the set only when the surface trusts it (`denominator.setCounted`, for the set
802
- # it picked), and finds a stale name on any surface read from the source, against the whole public
803
- # surface (the `all` set), not the set it counts.
826
+ # it picked). Both branches find a stale name against the whole public surface (`allSet`), not the set
827
+ # they count: the `all` set and every name on an `exports` record, since a nested Python entry's names
828
+ # leave `all` but keep their records (score-signatures.py's `signature_universe` rule). A scoped-out
829
+ # name has no record, so it stays stale.
804
830
  inp["surfaceSet"] = args.surface_set
805
831
  if isinstance(sets.get("all"), list):
806
- inp["allSet"] = sets["all"]
832
+ records = data.get("exports") if isinstance(data.get("exports"), list) else []
833
+ on_records = [r.get("name") for r in records if isinstance(r, dict)]
834
+ inp["allSet"] = sorted({n for n in sets["all"] + on_records if isinstance(n, str) and n})
807
835
  guards = data.get("guards")
808
836
  deflation = guards.get("deflation") if isinstance(guards, dict) else None
809
837
  inp["sourceRead"] = isinstance(deflation, dict) and deflation.get("applicable") is True