sdr-grader 1.1.1__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.
- sdr_grader-1.1.1/.claude-plugin/plugin.json +13 -0
- sdr_grader-1.1.1/.github/ISSUE_TEMPLATE/bug_report.yml +73 -0
- sdr_grader-1.1.1/.github/ISSUE_TEMPLATE/config.yml +8 -0
- sdr_grader-1.1.1/.github/ISSUE_TEMPLATE/feature_request.yml +65 -0
- sdr_grader-1.1.1/.github/PULL_REQUEST_TEMPLATE.md +30 -0
- sdr_grader-1.1.1/.github/workflows/release.yml +79 -0
- sdr_grader-1.1.1/.github/workflows/test.yml +82 -0
- sdr_grader-1.1.1/.gitignore +70 -0
- sdr_grader-1.1.1/CHANGELOG.md +237 -0
- sdr_grader-1.1.1/CLAUDE.md +39 -0
- sdr_grader-1.1.1/CONTRIBUTING.md +92 -0
- sdr_grader-1.1.1/LICENSE +21 -0
- sdr_grader-1.1.1/PKG-INFO +253 -0
- sdr_grader-1.1.1/README.md +227 -0
- sdr_grader-1.1.1/SECURITY.md +51 -0
- sdr_grader-1.1.1/docs/ADAPTER_GUIDE.md +124 -0
- sdr_grader-1.1.1/docs/CALIBRATION_CORPUS.md +145 -0
- sdr_grader-1.1.1/docs/CHECK_FUNCTION_GUIDE.md +117 -0
- sdr_grader-1.1.1/docs/CI_INTEGRATION.md +104 -0
- sdr_grader-1.1.1/docs/CUSTOMIZATION.md +126 -0
- sdr_grader-1.1.1/docs/LEADERBOARDS.md +58 -0
- sdr_grader-1.1.1/docs/PLATFORM_COVERAGE.md +47 -0
- sdr_grader-1.1.1/docs/PROJECT_CONFIG.md +82 -0
- sdr_grader-1.1.1/docs/RUBRIC_AUDIT.md +315 -0
- sdr_grader-1.1.1/docs/RUBRIC_FORMAT.md +128 -0
- sdr_grader-1.1.1/docs/SUPPLEMENTARY_INPUTS.md +84 -0
- sdr_grader-1.1.1/docs/TREND_REPORTS.md +57 -0
- sdr_grader-1.1.1/docs/calibration_manifest.example.yaml +33 -0
- sdr_grader-1.1.1/docs/threshold_calibration.md +266 -0
- sdr_grader-1.1.1/examples/grade-aa-clean.html +542 -0
- sdr_grader-1.1.1/examples/grade-aa-messy.html +793 -0
- sdr_grader-1.1.1/examples/grade-cja-clean.html +542 -0
- sdr_grader-1.1.1/examples/grade-cja-messy.html +988 -0
- sdr_grader-1.1.1/examples/sample-report.html +882 -0
- sdr_grader-1.1.1/examples/templated-report.html +883 -0
- sdr_grader-1.1.1/examples/trend-example.html +618 -0
- sdr_grader-1.1.1/pyproject.toml +131 -0
- sdr_grader-1.1.1/scripts/aggregate_distributions.py +129 -0
- sdr_grader-1.1.1/scripts/build_aa_fixtures.py +322 -0
- sdr_grader-1.1.1/scripts/build_cja_fixtures.py +614 -0
- sdr_grader-1.1.1/scripts/calibrate_thresholds.py +578 -0
- sdr_grader-1.1.1/scripts/file_remaining_design_issues.sh +74 -0
- sdr_grader-1.1.1/scripts/generate_examples.py +32 -0
- sdr_grader-1.1.1/scripts/generate_grade_examples.py +62 -0
- sdr_grader-1.1.1/scripts/generate_trend_example.py +55 -0
- sdr_grader-1.1.1/scripts/sanitize_sdr.py +169 -0
- sdr_grader-1.1.1/skills/sdr-grader/README.md +47 -0
- sdr_grader-1.1.1/skills/sdr-grader/SKILL.md +83 -0
- sdr_grader-1.1.1/skills/sdr-grader/scripts/query_grade.py +237 -0
- sdr_grader-1.1.1/src/sdr_grader/__init__.py +3 -0
- sdr_grader-1.1.1/src/sdr_grader/__main__.py +6 -0
- sdr_grader-1.1.1/src/sdr_grader/adapters/__init__.py +0 -0
- sdr_grader-1.1.1/src/sdr_grader/adapters/aa.py +417 -0
- sdr_grader-1.1.1/src/sdr_grader/adapters/cja.py +569 -0
- sdr_grader-1.1.1/src/sdr_grader/cli/__init__.py +0 -0
- sdr_grader-1.1.1/src/sdr_grader/cli/exit_codes.py +6 -0
- sdr_grader-1.1.1/src/sdr_grader/cli/main.py +478 -0
- sdr_grader-1.1.1/src/sdr_grader/core/__init__.py +0 -0
- sdr_grader-1.1.1/src/sdr_grader/core/exceptions.py +17 -0
- sdr_grader-1.1.1/src/sdr_grader/core/grade_calc.py +102 -0
- sdr_grader-1.1.1/src/sdr_grader/core/grader.py +245 -0
- sdr_grader-1.1.1/src/sdr_grader/core/models.py +100 -0
- sdr_grader-1.1.1/src/sdr_grader/core/timeparse.py +31 -0
- sdr_grader-1.1.1/src/sdr_grader/data/distribution.json +18 -0
- sdr_grader-1.1.1/src/sdr_grader/input/__init__.py +0 -0
- sdr_grader-1.1.1/src/sdr_grader/input/detect.py +39 -0
- sdr_grader-1.1.1/src/sdr_grader/input/loader.py +142 -0
- sdr_grader-1.1.1/src/sdr_grader/input/shell_out.py +104 -0
- sdr_grader-1.1.1/src/sdr_grader/render/__init__.py +46 -0
- sdr_grader-1.1.1/src/sdr_grader/render/distribution.py +96 -0
- sdr_grader-1.1.1/src/sdr_grader/render/json_output.py +33 -0
- sdr_grader-1.1.1/src/sdr_grader/render/renderer.py +207 -0
- sdr_grader-1.1.1/src/sdr_grader/render/static/report.css +355 -0
- sdr_grader-1.1.1/src/sdr_grader/render/svg.py +76 -0
- sdr_grader-1.1.1/src/sdr_grader/render/templates/report.html.j2 +191 -0
- sdr_grader-1.1.1/src/sdr_grader/render/truncation.py +54 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/__init__.py +0 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/__init__.py +0 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/_helpers.py +165 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/attribution.py +237 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/calc_metrics.py +305 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/governance.py +439 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/naming.py +356 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/schema_hygiene.py +677 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/segments.py +260 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/checks/supplementary.py +84 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/engine.py +62 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/__init__.py +0 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/pragmatic/_meta.yaml +35 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/pragmatic/attribution.yaml +26 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/pragmatic/calc_metrics.yaml +55 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/pragmatic/governance.yaml +66 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/pragmatic/naming.yaml +53 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/pragmatic/schema_hygiene.yaml +83 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/pragmatic/segments.yaml +54 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/strict/_meta.yaml +38 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/strict/attribution.yaml +52 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/strict/calc_metrics.yaml +86 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/strict/governance.yaml +117 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/strict/naming.yaml +90 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/strict/schema_hygiene.yaml +139 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/packs/strict/segments.yaml +89 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/registry.py +74 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/rubric.py +329 -0
- sdr_grader-1.1.1/src/sdr_grader/rules/suppression.py +204 -0
- sdr_grader-1.1.1/src/sdr_grader/trend/__init__.py +19 -0
- sdr_grader-1.1.1/src/sdr_grader/trend/renderer.py +286 -0
- sdr_grader-1.1.1/src/sdr_grader/trend/runner.py +138 -0
- sdr_grader-1.1.1/src/sdr_grader/trend/templates/trend.html.j2 +119 -0
- sdr_grader-1.1.1/tests/_rule_test_helpers.py +107 -0
- sdr_grader-1.1.1/tests/conftest.py +1 -0
- sdr_grader-1.1.1/tests/fixtures/__init__.py +0 -0
- sdr_grader-1.1.1/tests/fixtures/aa_snapshot_clean.json +694 -0
- sdr_grader-1.1.1/tests/fixtures/aa_snapshot_messy.json +1236 -0
- sdr_grader-1.1.1/tests/fixtures/cja_snapshot_clean.json +1290 -0
- sdr_grader-1.1.1/tests/fixtures/cja_snapshot_messy.json +10434 -0
- sdr_grader-1.1.1/tests/fixtures/demo_report.py +318 -0
- sdr_grader-1.1.1/tests/test_adapter_fuzz.py +170 -0
- sdr_grader-1.1.1/tests/test_adapters_aa.py +225 -0
- sdr_grader-1.1.1/tests/test_adapters_cja.py +351 -0
- sdr_grader-1.1.1/tests/test_calibration_attr_measurements.py +170 -0
- sdr_grader-1.1.1/tests/test_cli.py +302 -0
- sdr_grader-1.1.1/tests/test_cross_platform_parity.py +173 -0
- sdr_grader-1.1.1/tests/test_cycle_groups.py +38 -0
- sdr_grader-1.1.1/tests/test_detect_and_registry.py +98 -0
- sdr_grader-1.1.1/tests/test_distribution.py +172 -0
- sdr_grader-1.1.1/tests/test_grade_calc.py +174 -0
- sdr_grader-1.1.1/tests/test_input_modes.py +322 -0
- sdr_grader-1.1.1/tests/test_render_cap.py +87 -0
- sdr_grader-1.1.1/tests/test_renderer.py +107 -0
- sdr_grader-1.1.1/tests/test_rubric_validation.py +63 -0
- sdr_grader-1.1.1/tests/test_rules_attribution.py +180 -0
- sdr_grader-1.1.1/tests/test_rules_calc_metrics.py +141 -0
- sdr_grader-1.1.1/tests/test_rules_governance.py +338 -0
- sdr_grader-1.1.1/tests/test_rules_naming.py +235 -0
- sdr_grader-1.1.1/tests/test_rules_schema_hygiene.py +148 -0
- sdr_grader-1.1.1/tests/test_rules_schema_hygiene_extras.py +543 -0
- sdr_grader-1.1.1/tests/test_rules_segments.py +127 -0
- sdr_grader-1.1.1/tests/test_rules_supplementary.py +98 -0
- sdr_grader-1.1.1/tests/test_skill_helper.py +119 -0
- sdr_grader-1.1.1/tests/test_smoke.py +5 -0
- sdr_grader-1.1.1/tests/test_suppression.py +178 -0
- sdr_grader-1.1.1/tests/test_svg_charts.py +32 -0
- sdr_grader-1.1.1/tests/test_timeparse.py +47 -0
- sdr_grader-1.1.1/tests/test_trend.py +178 -0
- sdr_grader-1.1.1/uv.lock +602 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://claude.ai/schemas/plugin.json",
|
|
3
|
+
"name": "sdr-grader",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Follow-up question support for sdr-grader's --json grade output. Filter findings, look up remediations, and compare two grades from inside Claude Code.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Brian Au",
|
|
8
|
+
"url": "https://github.com/brian-a-au/sdr-grader"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"homepage": "https://github.com/brian-a-au/sdr-grader",
|
|
12
|
+
"skills": ["skills/sdr-grader"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: A rule grades incorrectly, the CLI crashes, or output is wrong.
|
|
3
|
+
title: "[bug] "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for taking the time to file a bug. A reproducible snapshot
|
|
10
|
+
is the single most useful thing you can attach.
|
|
11
|
+
|
|
12
|
+
**Do not paste a real production snapshot.** Use
|
|
13
|
+
`scripts/sanitize_sdr.py` to scrub tenant identifiers, then trim
|
|
14
|
+
to the smallest shape that still reproduces the bug. If the
|
|
15
|
+
snapshot is sensitive even after scrubbing, open a
|
|
16
|
+
[private security advisory](https://github.com/brian-a-au/sdr-grader/security/advisories/new)
|
|
17
|
+
instead.
|
|
18
|
+
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: what-happened
|
|
21
|
+
attributes:
|
|
22
|
+
label: What happened
|
|
23
|
+
description: What did the grader do? What did you expect it to do?
|
|
24
|
+
placeholder: |
|
|
25
|
+
Ran `sdr-grader snapshot.json --output grade.html`. SCH-003 fired
|
|
26
|
+
with "170 components lack descriptions" but the snapshot has
|
|
27
|
+
descriptions on every component.
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
30
|
+
|
|
31
|
+
- type: textarea
|
|
32
|
+
id: reproduction
|
|
33
|
+
attributes:
|
|
34
|
+
label: Reproduction
|
|
35
|
+
description: |
|
|
36
|
+
Minimal command + minimal snapshot. Inline the snapshot in a code
|
|
37
|
+
block if it's small, or attach it.
|
|
38
|
+
render: shell
|
|
39
|
+
validations:
|
|
40
|
+
required: true
|
|
41
|
+
|
|
42
|
+
- type: input
|
|
43
|
+
id: version
|
|
44
|
+
attributes:
|
|
45
|
+
label: sdr-grader version
|
|
46
|
+
description: Output of `sdr-grader --version` (or commit SHA if running from source).
|
|
47
|
+
placeholder: "1.0.0"
|
|
48
|
+
validations:
|
|
49
|
+
required: true
|
|
50
|
+
|
|
51
|
+
- type: dropdown
|
|
52
|
+
id: platform
|
|
53
|
+
attributes:
|
|
54
|
+
label: Platform
|
|
55
|
+
options:
|
|
56
|
+
- CJA (cja_auto_sdr snapshot)
|
|
57
|
+
- AA (aa_auto_sdr snapshot)
|
|
58
|
+
- Both / not platform-specific
|
|
59
|
+
validations:
|
|
60
|
+
required: true
|
|
61
|
+
|
|
62
|
+
- type: input
|
|
63
|
+
id: pack
|
|
64
|
+
attributes:
|
|
65
|
+
label: Rubric pack
|
|
66
|
+
description: Which pack was active? Default is `strict`.
|
|
67
|
+
placeholder: "strict"
|
|
68
|
+
|
|
69
|
+
- type: textarea
|
|
70
|
+
id: extra
|
|
71
|
+
attributes:
|
|
72
|
+
label: Anything else
|
|
73
|
+
description: Logs, screenshots of the rendered report, related issues.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Security vulnerability
|
|
4
|
+
url: https://github.com/brian-a-au/sdr-grader/security/advisories/new
|
|
5
|
+
about: Please report security issues privately via a GitHub Security Advisory, not a public issue.
|
|
6
|
+
- name: False positive on a private snapshot
|
|
7
|
+
url: https://github.com/brian-a-au/sdr-grader/security/advisories/new
|
|
8
|
+
about: If a rule fires incorrectly on a snapshot you cannot share publicly, open a private advisory and we'll work out a sanitized reduction together. See SECURITY.md.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Feature or rule proposal
|
|
2
|
+
description: Propose a new rule, new check kind, or other enhancement.
|
|
3
|
+
title: "[feature] "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Open an issue **before** opening a PR for new rules or structural
|
|
10
|
+
changes — it saves us both time vs. arriving at the same
|
|
11
|
+
conclusion through review feedback. See `CONTRIBUTING.md` for
|
|
12
|
+
what's PR-able directly vs. maintainer-gated.
|
|
13
|
+
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: proposal
|
|
16
|
+
attributes:
|
|
17
|
+
label: What are you proposing
|
|
18
|
+
description: One paragraph. New rule? New check kind? New CLI flag? Something else?
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
|
|
22
|
+
- type: textarea
|
|
23
|
+
id: motivation
|
|
24
|
+
attributes:
|
|
25
|
+
label: Motivation
|
|
26
|
+
description: |
|
|
27
|
+
What broken-implementation pattern does this catch? Why does the
|
|
28
|
+
current rule set miss it?
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
31
|
+
|
|
32
|
+
- type: dropdown
|
|
33
|
+
id: shape
|
|
34
|
+
attributes:
|
|
35
|
+
label: If this is a new rule, what does it measure?
|
|
36
|
+
description: |
|
|
37
|
+
See `CONTRIBUTING.md` invariants. Cardinality rules ("you have
|
|
38
|
+
more than N segments") are rejected by default — the same number
|
|
39
|
+
is healthy for one tenant and pathological for another.
|
|
40
|
+
options:
|
|
41
|
+
- Shape (structure of a definition, e.g. nesting depth)
|
|
42
|
+
- Ratio (e.g. % of components missing descriptions)
|
|
43
|
+
- Correctness (e.g. formula references a deleted component)
|
|
44
|
+
- Cardinality (raw count — likely to be rejected)
|
|
45
|
+
- Not applicable (not a new rule)
|
|
46
|
+
validations:
|
|
47
|
+
required: true
|
|
48
|
+
|
|
49
|
+
- type: textarea
|
|
50
|
+
id: example
|
|
51
|
+
attributes:
|
|
52
|
+
label: Example
|
|
53
|
+
description: |
|
|
54
|
+
Synthetic fixture or sketch of what the firing condition looks
|
|
55
|
+
like. If this needs calibration data, say so — provisional
|
|
56
|
+
thresholds are fine in an initial PR.
|
|
57
|
+
render: shell
|
|
58
|
+
|
|
59
|
+
- type: textarea
|
|
60
|
+
id: alternatives
|
|
61
|
+
attributes:
|
|
62
|
+
label: Alternatives considered
|
|
63
|
+
description: |
|
|
64
|
+
Existing rules that almost cover this? Other ways to surface the
|
|
65
|
+
same signal (e.g. supplementary input vs. core rule)?
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this PR do, in 1-3 sentences? Link the issue it closes. -->
|
|
4
|
+
|
|
5
|
+
## Type of change
|
|
6
|
+
|
|
7
|
+
- [ ] Bug fix (incorrect grading, adapter crash, renderer regression)
|
|
8
|
+
- [ ] New rule (also opened an issue first per CONTRIBUTING.md)
|
|
9
|
+
- [ ] Threshold / severity tweak (requires maintainer recalibration — explained in the issue)
|
|
10
|
+
- [ ] Docs / examples / CI
|
|
11
|
+
- [ ] Other (describe)
|
|
12
|
+
|
|
13
|
+
## Invariant check
|
|
14
|
+
|
|
15
|
+
These are non-negotiable per CONTRIBUTING.md. Confirm:
|
|
16
|
+
|
|
17
|
+
- [ ] No randomness or `datetime.now()` in graded output (determinism contract)
|
|
18
|
+
- [ ] No new cardinality rules (`len(X) > k` shapes are rejected)
|
|
19
|
+
- [ ] `render/` does not import from `rules/` or `core/grader.py`
|
|
20
|
+
|
|
21
|
+
## Tests + examples
|
|
22
|
+
|
|
23
|
+
- [ ] `uv run pytest` passes locally
|
|
24
|
+
- [ ] `uv run ruff check` passes
|
|
25
|
+
- [ ] If rules or renderer changed, examples regenerated via `scripts/generate_*.py` and the diff is committed (the `examples-drift` CI gate will fail otherwise)
|
|
26
|
+
|
|
27
|
+
## Notes for the reviewer
|
|
28
|
+
|
|
29
|
+
<!-- Anything non-obvious: a tricky calibration choice, a subtle invariant
|
|
30
|
+
relied on, a fixture that needed updating, a follow-up issue you opened. -->
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*.*.*'
|
|
7
|
+
|
|
8
|
+
# Minimal default; each job opts into exactly the scopes it needs.
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write # create the GitHub release and attach the dist assets
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v3
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
run: uv python install 3.12
|
|
25
|
+
|
|
26
|
+
- name: Sync dependencies
|
|
27
|
+
run: uv sync --all-extras --dev
|
|
28
|
+
|
|
29
|
+
- name: Lint
|
|
30
|
+
run: uv run ruff check
|
|
31
|
+
|
|
32
|
+
- name: Test
|
|
33
|
+
run: uv run pytest --cov=src/sdr_grader --cov-report=term
|
|
34
|
+
|
|
35
|
+
- name: Build distributions
|
|
36
|
+
run: uv build
|
|
37
|
+
|
|
38
|
+
- name: Drop uv's stray dist/.gitignore
|
|
39
|
+
# `uv build` writes a `.gitignore` (containing `*`) into dist/. Left
|
|
40
|
+
# in place it rides along into the uploaded artifact and shows up as
|
|
41
|
+
# a `default.gitignore` release asset.
|
|
42
|
+
run: rm -f dist/.gitignore
|
|
43
|
+
|
|
44
|
+
- name: Upload built distributions as a workflow artifact
|
|
45
|
+
uses: actions/upload-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: dist
|
|
48
|
+
path: dist/
|
|
49
|
+
if-no-files-found: error
|
|
50
|
+
|
|
51
|
+
- name: Upload distributions to GitHub release
|
|
52
|
+
uses: softprops/action-gh-release@v2
|
|
53
|
+
with:
|
|
54
|
+
files: |
|
|
55
|
+
dist/*.whl
|
|
56
|
+
dist/*.tar.gz
|
|
57
|
+
generate_release_notes: true
|
|
58
|
+
|
|
59
|
+
publish:
|
|
60
|
+
needs: build
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
# The `pypi` environment gates this job. Configure it on GitHub
|
|
63
|
+
# (Settings -> Environments -> pypi) with a required reviewer so the
|
|
64
|
+
# irreversible PyPI upload waits for a manual approval, and keep the
|
|
65
|
+
# matching "Environment name: pypi" on the PyPI trusted publisher.
|
|
66
|
+
environment:
|
|
67
|
+
name: pypi
|
|
68
|
+
url: https://pypi.org/p/sdr-grader
|
|
69
|
+
permissions:
|
|
70
|
+
id-token: write # OIDC token for PyPI trusted publishing; nothing else
|
|
71
|
+
steps:
|
|
72
|
+
- name: Download built distributions
|
|
73
|
+
uses: actions/download-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: dist
|
|
76
|
+
path: dist/
|
|
77
|
+
|
|
78
|
+
- name: Publish to PyPI
|
|
79
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v3
|
|
19
|
+
with:
|
|
20
|
+
enable-cache: true
|
|
21
|
+
# The default prune strips pre-built wheels from the saved cache,
|
|
22
|
+
# so every run re-downloaded them from PyPI despite a "cache hit".
|
|
23
|
+
prune-cache: false
|
|
24
|
+
|
|
25
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
26
|
+
run: uv python install ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Sync dependencies
|
|
29
|
+
run: uv sync --all-extras --dev
|
|
30
|
+
|
|
31
|
+
- name: Lint
|
|
32
|
+
run: uv run ruff check
|
|
33
|
+
|
|
34
|
+
- name: Test
|
|
35
|
+
run: uv run pytest --cov=src/sdr_grader --cov-report=term --cov-report=xml
|
|
36
|
+
|
|
37
|
+
- name: Upload coverage XML
|
|
38
|
+
if: matrix.python-version == '3.12'
|
|
39
|
+
uses: actions/upload-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: coverage-xml
|
|
42
|
+
path: coverage.xml
|
|
43
|
+
if-no-files-found: warn
|
|
44
|
+
|
|
45
|
+
examples-drift:
|
|
46
|
+
# Regenerate every example artifact and fail if anything changed.
|
|
47
|
+
# Catches commits that change rules / fixtures / renderer without
|
|
48
|
+
# refreshing the committed examples/ directory.
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
|
|
53
|
+
- name: Install uv
|
|
54
|
+
uses: astral-sh/setup-uv@v3
|
|
55
|
+
with:
|
|
56
|
+
enable-cache: true
|
|
57
|
+
# The default prune strips pre-built wheels from the saved cache,
|
|
58
|
+
# so every run re-downloaded them from PyPI despite a "cache hit".
|
|
59
|
+
prune-cache: false
|
|
60
|
+
|
|
61
|
+
- name: Set up Python
|
|
62
|
+
run: uv python install 3.12
|
|
63
|
+
|
|
64
|
+
- name: Sync dependencies
|
|
65
|
+
run: uv sync --all-extras --dev
|
|
66
|
+
|
|
67
|
+
- name: Regenerate fixtures
|
|
68
|
+
run: uv run python scripts/build_cja_fixtures.py
|
|
69
|
+
|
|
70
|
+
- name: Regenerate examples
|
|
71
|
+
run: |
|
|
72
|
+
uv run python scripts/generate_examples.py
|
|
73
|
+
uv run python scripts/generate_grade_examples.py
|
|
74
|
+
uv run python scripts/generate_trend_example.py
|
|
75
|
+
|
|
76
|
+
- name: Fail on diff
|
|
77
|
+
run: |
|
|
78
|
+
if ! git diff --exit-code -- examples/ tests/fixtures/; then
|
|
79
|
+
echo "::error::Generated examples or fixtures drifted from committed copies."
|
|
80
|
+
echo "Run the relevant generator scripts locally and commit the result."
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Local reference artifacts (kept in working directory, not tracked).
|
|
2
|
+
# These are the inputs the SPEC describes — the visual contract reference and
|
|
3
|
+
# the spec itself. Phase 1 will copy them into src/sdr_grader/render/ paths.
|
|
4
|
+
/render.py
|
|
5
|
+
/report.css
|
|
6
|
+
/report.html.j2
|
|
7
|
+
/sample-report.html
|
|
8
|
+
/templated-report.html
|
|
9
|
+
/SPEC.md
|
|
10
|
+
|
|
11
|
+
# macOS
|
|
12
|
+
.DS_Store
|
|
13
|
+
|
|
14
|
+
# Python
|
|
15
|
+
__pycache__/
|
|
16
|
+
*.py[cod]
|
|
17
|
+
*$py.class
|
|
18
|
+
*.so
|
|
19
|
+
.Python
|
|
20
|
+
build/
|
|
21
|
+
dist/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.eggs/
|
|
24
|
+
*.egg
|
|
25
|
+
MANIFEST
|
|
26
|
+
|
|
27
|
+
# uv / virtualenvs
|
|
28
|
+
.venv/
|
|
29
|
+
venv/
|
|
30
|
+
env/
|
|
31
|
+
|
|
32
|
+
# Testing
|
|
33
|
+
.pytest_cache/
|
|
34
|
+
.hypothesis/
|
|
35
|
+
.coverage
|
|
36
|
+
.coverage.*
|
|
37
|
+
htmlcov/
|
|
38
|
+
coverage.xml
|
|
39
|
+
.tox/
|
|
40
|
+
|
|
41
|
+
# Linters / type checkers
|
|
42
|
+
.ruff_cache/
|
|
43
|
+
.mypy_cache/
|
|
44
|
+
.pyre/
|
|
45
|
+
|
|
46
|
+
# IDE
|
|
47
|
+
.idea/
|
|
48
|
+
.vscode/
|
|
49
|
+
*.swp
|
|
50
|
+
*.swo
|
|
51
|
+
|
|
52
|
+
# Local-only generated artifacts
|
|
53
|
+
/grade-*.html
|
|
54
|
+
/grade-*.json
|
|
55
|
+
|
|
56
|
+
# Environment
|
|
57
|
+
.env
|
|
58
|
+
.env.local
|
|
59
|
+
|
|
60
|
+
# Claude Code "superpowers" plugin artifacts and plans — local tooling only,
|
|
61
|
+
# never committed. Matches any `superpowers/` directory at any depth
|
|
62
|
+
# (e.g. `/superpowers/`, `docs/superpowers/plans/`), including the
|
|
63
|
+
# dot-prefixed `.superpowers/` working directory the plugin writes to.
|
|
64
|
+
**/superpowers/
|
|
65
|
+
.superpowers/
|
|
66
|
+
|
|
67
|
+
# Private production SDR snapshots used for rubric calibration.
|
|
68
|
+
# Real customer data; never committed. See the calibration plan in
|
|
69
|
+
# `docs/superpowers/plans/` for the loader contract.
|
|
70
|
+
/tests/fixtures/private/
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes follow the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
4
|
+
spirit. The version numbers follow [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## 1.1.1 — 2026-07-13
|
|
7
|
+
|
|
8
|
+
A packaging-only release. No rules, checks, or grades change, so grades
|
|
9
|
+
are identical to 1.1.0.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Local working artifacts no longer ship in the source distribution.**
|
|
14
|
+
The `.superpowers/` and `.hypothesis/` directories were bundled into the
|
|
15
|
+
sdist because `.gitignore` did not cover them, and hatchling reads
|
|
16
|
+
`.gitignore` to decide what the sdist contains. They are now ignored,
|
|
17
|
+
and `pyproject.toml` excludes them from the sdist target as a second
|
|
18
|
+
guard. The sdist is uploaded to PyPI and is world-readable regardless of
|
|
19
|
+
the git repository's visibility, so these never belonged in it. The
|
|
20
|
+
wheel was already clean and is unchanged.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Richer PyPI metadata.** Added trove classifiers and keywords to
|
|
25
|
+
`pyproject.toml`. No runtime change.
|
|
26
|
+
|
|
27
|
+
## 1.1.0 — 2026-07-11
|
|
28
|
+
|
|
29
|
+
This release adds six rules, removes two rules that a corpus audit showed
|
|
30
|
+
could not tell a good implementation from a bad one, and fixes a wide set
|
|
31
|
+
of bugs in the adapters, the input pipeline, and the renderer. The default
|
|
32
|
+
packs now hold 30 rules across the same 6 categories. Grades can shift
|
|
33
|
+
from 1.0.0 because the default packs changed.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **Six new rules** in both default packs:
|
|
38
|
+
- `SCH-007` and `ATTR-004` grade CJA Data View settings.
|
|
39
|
+
- `SCH-008` finds cycles between CJA derived fields.
|
|
40
|
+
- `SCH-009` finds CJA derived fields that reference components that do
|
|
41
|
+
not exist.
|
|
42
|
+
- `GOV-007` and `GOV-008` flag calculated metrics and segments that are
|
|
43
|
+
shared but never approved.
|
|
44
|
+
- **Default output filenames keyed to the report id.** When you do not
|
|
45
|
+
pass `--output`, the CLI names the file `grade-<report id>.html`. The
|
|
46
|
+
old default used a timestamp, so a batch run that graded several
|
|
47
|
+
instances within the same second wrote them all to one file. (#2)
|
|
48
|
+
- **Capped component lists in the report.** A finding now shows at most
|
|
49
|
+
50 affected components plus a line with the count of the hidden ones.
|
|
50
|
+
The `--json` output keeps the full list. (#5)
|
|
51
|
+
- **Full inventory in shell-out mode.** Runs started with `--dataview` or
|
|
52
|
+
`--rsid` now pass `--include-all-inventory` to the snapshot tool.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- **`ATTR-001` and `ATTR-002` are out of the default packs.** A corpus
|
|
57
|
+
audit (`docs/RUBRIC_AUDIT.md`) showed that `ATTR-001` fired on 100% of
|
|
58
|
+
the metrics it watched, and that every fixture scored the same on
|
|
59
|
+
`ATTR-002`, so its threshold could not separate anything. Both check
|
|
60
|
+
functions stay registered for custom packs.
|
|
61
|
+
- **`NAME-001` and `NAME-003` grade again.** The CJA adapter now parses
|
|
62
|
+
tags that arrive as JSON-encoded strings, which is what real snapshots
|
|
63
|
+
contain. Before this fix the two rules matched nothing.
|
|
64
|
+
- **Faster rendering of large reports.** The renderer caches compiled
|
|
65
|
+
templates and CSS, and the report skips layout work for findings that
|
|
66
|
+
are off-screen.
|
|
67
|
+
- **Docs reorganized.** Reference material moved out of the README into
|
|
68
|
+
`docs/`, including a customization hub, platform coverage, supplementary
|
|
69
|
+
inputs, leaderboards, and the rubric audit.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- **HTML escaping.** The report and trend templates rendered values
|
|
74
|
+
without escaping, and the comparison chart did not escape category
|
|
75
|
+
labels in its SVG. Both are fixed, so snapshot content can no longer
|
|
76
|
+
inject markup into a report.
|
|
77
|
+
- **Input handling.** Snapshot files with a UTF-8 BOM load. Timestamps
|
|
78
|
+
with UTC offsets or fractional seconds parse. A snapshot whose platform
|
|
79
|
+
cannot be determined raises a clear error instead of being guessed.
|
|
80
|
+
Directory mode ranks all snapshots on one timestamp scale.
|
|
81
|
+
- **Shell-out mode.** The subprocess call has a timeout, decodes output
|
|
82
|
+
as UTF-8, surfaces warnings from the snapshot tool, and reports decode
|
|
83
|
+
failures as the standard invalid-snapshot error.
|
|
84
|
+
- **Rule engine.** Pattern and target params are validated when the
|
|
85
|
+
rubric loads instead of failing mid-run. Cycle detection for `SCH-008`
|
|
86
|
+
and `SEG-004` is iterative and deterministic. Expiration blocks with
|
|
87
|
+
NaN or Infinity `numPeriods` no longer crash.
|
|
88
|
+
- **AA adapter.** It rejects exports whose dimension, metric, calculated
|
|
89
|
+
metric, or segment sections are missing or not lists. It guards against
|
|
90
|
+
wrongly typed tags, reference lists, and numbers. It counts nesting
|
|
91
|
+
depth the same way the CJA adapter does. It renders nested formula text
|
|
92
|
+
readably and skips blank classification tags.
|
|
93
|
+
- **CJA adapter.** Derived-field deduplication normalizes IDs before
|
|
94
|
+
comparing them.
|
|
95
|
+
- **CLI.** `--fail-below` works in trend mode, and trend mode rejects
|
|
96
|
+
flags it does not support.
|
|
97
|
+
|
|
98
|
+
### Discipline
|
|
99
|
+
|
|
100
|
+
- 402 tests pass and ruff is clean.
|
|
101
|
+
- Determinism holds. The same snapshot and rubric still produce
|
|
102
|
+
byte-identical HTML and JSON.
|
|
103
|
+
|
|
104
|
+
## 1.0.0 — 2026-05-20
|
|
105
|
+
|
|
106
|
+
First public release. The grader covers the full surface the design SPEC
|
|
107
|
+
laid out, plus a Claude Code skill bundle for follow-up question support.
|
|
108
|
+
Default rubric thresholds are calibrated against a 108-snapshot corpus of
|
|
109
|
+
real CJA + AA production implementations — see
|
|
110
|
+
[`docs/threshold_calibration.md`](docs/threshold_calibration.md) for the
|
|
111
|
+
distributions and the per-rule confidence rating behind each threshold.
|
|
112
|
+
|
|
113
|
+
### Added
|
|
114
|
+
|
|
115
|
+
- **Deterministic, rule-based linter** for Adobe CJA and AA implementations.
|
|
116
|
+
Reads `cja_auto_sdr` / `aa_auto_sdr` JSON snapshots; emits a single
|
|
117
|
+
self-contained HTML report card and a parallel machine-readable JSON.
|
|
118
|
+
- **26 rules across 6 categories**: schema hygiene, naming consistency,
|
|
119
|
+
segment complexity, calc metric maintainability, attribution coverage,
|
|
120
|
+
governance posture. Every rule in the default packs grades against
|
|
121
|
+
data the snapshot itself carries; check functions that need external
|
|
122
|
+
evidence (Launch exports, cardinality, ownership, downstream usage,
|
|
123
|
+
SDR docs) ship registered but unwired so operators can include them
|
|
124
|
+
in a forked pack.
|
|
125
|
+
- **No cardinality rules.** Rules measure shape, ratio, or correctness —
|
|
126
|
+
never raw counts. See SPEC §11 for the principle and rationale.
|
|
127
|
+
- **Two bundled rubric packs (v1.0)**: `strict` (master-cert-grade,
|
|
128
|
+
calibrated p75–p90 thresholds) and `pragmatic` (sanity-check,
|
|
129
|
+
calibrated p90–p95 thresholds, same rule IDs).
|
|
130
|
+
- **Four input modes**: file path, snapshot directory (with
|
|
131
|
+
`--at TIMESTAMP` and `--trend`), shell-out via `--dataview` /
|
|
132
|
+
`--rsid`, and stdin.
|
|
133
|
+
- **Trend reports**: `--trend` walks a snapshot directory chronologically
|
|
134
|
+
and renders an HTML trajectory with sparklines, per-category deltas,
|
|
135
|
+
and a findings churn summary.
|
|
136
|
+
- **Distribution context**: `--distribution-data PATH` (or `bundled`)
|
|
137
|
+
populates the report's overall histogram and category comparison
|
|
138
|
+
charts from a percentile data file.
|
|
139
|
+
- **Project-level suppression**: `.sdr-grader.yaml` lets operators
|
|
140
|
+
suppress rules, override severities, and rebalance category weights.
|
|
141
|
+
Skipped rules surface in the rendered methodology section.
|
|
142
|
+
- **Supplementary inputs**: `--extra-input KEY=PATH` attaches arbitrary
|
|
143
|
+
JSON files under `Implementation.supplementary_data[KEY]`. The shape
|
|
144
|
+
and key are entirely the rule's contract; the default packs don't
|
|
145
|
+
consume them. Operators who want to grade external evidence write a
|
|
146
|
+
custom rubric pack that targets the registered check functions.
|
|
147
|
+
- **JSON output**: `--json PATH` writes the full Report data structure
|
|
148
|
+
for CI dashboards and downstream tooling. Datetimes normalize to UTC
|
|
149
|
+
ISO-8601 with `Z` suffix.
|
|
150
|
+
- **CI integration**: `--fail-below GRADE` exits 2 when the grade drops
|
|
151
|
+
below the threshold letter, suitable for PR gates.
|
|
152
|
+
- **Claude Code skill bundle** at `skills/sdr-grader/`: SKILL.md plus a
|
|
153
|
+
bundled `query_grade.py` helper for filtering findings, looking up
|
|
154
|
+
remediations, and comparing two grade JSONs from inside Claude Code.
|
|
155
|
+
- **Documentation set**: `docs/RUBRIC_FORMAT.md`, `docs/CHECK_FUNCTION_GUIDE.md`,
|
|
156
|
+
`docs/ADAPTER_GUIDE.md`, `docs/CI_INTEGRATION.md`, plus README quickstart.
|
|
157
|
+
- **Examples**: clean and messy grade cards per platform
|
|
158
|
+
(`examples/grade-cja-clean.html` A 100%, `examples/grade-cja-messy.html`
|
|
159
|
+
F 44%, `examples/grade-aa-clean.html` A 100%, `examples/grade-aa-messy.html`
|
|
160
|
+
F 39%), a multi-snapshot `examples/trend-example.html`, the original
|
|
161
|
+
visual-contract reference `examples/sample-report.html`, and the
|
|
162
|
+
renderer-output golden `examples/templated-report.html`.
|
|
163
|
+
|
|
164
|
+
### Discipline
|
|
165
|
+
|
|
166
|
+
- **Determinism is testable.** The same snapshot + rubric produces
|
|
167
|
+
byte-identical HTML and JSON; tests assert this across two runs.
|
|
168
|
+
- **261 tests** cover adapters, rules, engine, grader, CLI, trend
|
|
169
|
+
pipeline, distribution context, supplementary inputs, and the skill
|
|
170
|
+
helper. All pass; ruff clean.
|
|
171
|
+
- **Phase discipline preserved**: each commit produced a working,
|
|
172
|
+
reviewable artifact. The full commit graph from scaffold to 1.0.0 is
|
|
173
|
+
visible in the git history.
|
|
174
|
+
|
|
175
|
+
### Calibration-driven design decisions
|
|
176
|
+
|
|
177
|
+
Calibration against a 108-snapshot corpus of real CJA + AA production
|
|
178
|
+
implementations exposed three rule design problems that this release
|
|
179
|
+
addresses:
|
|
180
|
+
|
|
181
|
+
- **Adobe APIs don't expose `owner` on inline dimensions / metrics.**
|
|
182
|
+
Every tenant in the corpus measured 100% missing owners even after
|
|
183
|
+
normalizing the `"Unknown User"` sentinel. `GOV-004 missing_owners`
|
|
184
|
+
has been moved out of the default packs (still registered for
|
|
185
|
+
operators with their own ownership data).
|
|
186
|
+
- **SDR snapshots don't carry downstream usage.** "Orphan" detection
|
|
187
|
+
for segments and calculated metrics in the corpus showed every
|
|
188
|
+
tenant at 100% — segments and calc metrics in production are
|
|
189
|
+
referenced by Workspace projects, dashboards, and alerts, none of
|
|
190
|
+
which the SDR captures. `SEG-003 orphan_segments` and
|
|
191
|
+
`CALC-005 orphan_calc_metrics` have been moved out of the default
|
|
192
|
+
packs.
|
|
193
|
+
- **Description discipline is universally poor.** ≥90% of real
|
|
194
|
+
segment-bearing tenants and ≈100% of calc-metric-bearing tenants
|
|
195
|
+
have 100% of those components missing descriptions.
|
|
196
|
+
`SEG-005 segments_missing_descriptions` and
|
|
197
|
+
`CALC-001 calc_metrics_missing_descriptions` are kept in the default
|
|
198
|
+
packs at low severity with near-saturation thresholds (0.95 strict,
|
|
199
|
+
1.0 pragmatic) — they flag the most egregious tenants without
|
|
200
|
+
swamping the grade.
|
|
201
|
+
|
|
202
|
+
### Registered but not in the default packs
|
|
203
|
+
|
|
204
|
+
These check functions are registered and tested, but the default
|
|
205
|
+
`strict` / `pragmatic` packs don't reference them — they require
|
|
206
|
+
evidence the snapshot itself doesn't carry. Operators with that
|
|
207
|
+
evidence can include them in a custom rubric pack:
|
|
208
|
+
|
|
209
|
+
- `orphan_segments` (slot `SEG-003`) and `orphan_calc_metrics` (slot
|
|
210
|
+
`CALC-005`) — require downstream Workspace / dashboard usage data.
|
|
211
|
+
- `missing_owners` (slot `GOV-004`) — requires owner attribution data
|
|
212
|
+
that Adobe's APIs don't expose on dims/metrics.
|
|
213
|
+
- `doc_drift` (slot `GOV-006`) — reads `last_sdr_update_at` (param),
|
|
214
|
+
`supplementary_data['sdr']['last_updated_at']`, or
|
|
215
|
+
`metadata['SDR Last Updated']`.
|
|
216
|
+
- `cardinality_concerns` (slot `SCH-006`) — reads
|
|
217
|
+
`supplementary_data['cardinality']` (a `component_id -> int` map).
|
|
218
|
+
- `launch_required_data_elements` (slot `LAUNCH-001`) — reads
|
|
219
|
+
`supplementary_data['launch']`; the canonical worked example for the
|
|
220
|
+
`--extra-input` extension pattern.
|
|
221
|
+
- `calc_deprecated_allocations` (slot `CALC-022`) — needs a concrete
|
|
222
|
+
`deprecated_allocations` set via params; default ships placeholder
|
|
223
|
+
values.
|
|
224
|
+
|
|
225
|
+
### Known limitations
|
|
226
|
+
|
|
227
|
+
- `cja_auto_sdr` / `aa_auto_sdr` are required to produce the JSON
|
|
228
|
+
snapshots the grader consumes; they are separate projects.
|
|
229
|
+
- The bundled `data/distribution.json` ships seed percentile data.
|
|
230
|
+
`scripts/aggregate_distributions.py` lets teams build their own
|
|
231
|
+
internal leaderboards from a directory of grade JSONs;
|
|
232
|
+
`--distribution-data PATH` plugs the result into the report. A
|
|
233
|
+
centralized opt-in submission service is out of scope for this repo.
|
|
234
|
+
- README screenshots require manual capture (open
|
|
235
|
+
`examples/grade-cja-messy.html` in a browser and screenshot the page).
|
|
236
|
+
Embedded SVG sparklines + the inlined CSS make the report itself a
|
|
237
|
+
high-fidelity preview when rendered.
|