moult 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +44 -0
- data/LICENSE.txt +201 -0
- data/NOTICE +4 -0
- data/README.md +331 -0
- data/exe/moult +6 -0
- data/lib/moult/abc.rb +133 -0
- data/lib/moult/boundaries/packwerk.rb +114 -0
- data/lib/moult/boundaries/severity.rb +87 -0
- data/lib/moult/boundaries.rb +77 -0
- data/lib/moult/boundaries_report.rb +106 -0
- data/lib/moult/churn.rb +52 -0
- data/lib/moult/cli/boundaries_command.rb +83 -0
- data/lib/moult/cli/coverage_command.rb +101 -0
- data/lib/moult/cli/dead_code_command.rb +112 -0
- data/lib/moult/cli/duplication_command.rb +92 -0
- data/lib/moult/cli/flags_command.rb +95 -0
- data/lib/moult/cli/gate_command.rb +113 -0
- data/lib/moult/cli/health_command.rb +117 -0
- data/lib/moult/cli/hotspots_command.rb +104 -0
- data/lib/moult/cli.rb +102 -0
- data/lib/moult/clones.rb +91 -0
- data/lib/moult/cloud_upload.rb +29 -0
- data/lib/moult/confidence/rules.rb +128 -0
- data/lib/moult/confidence.rb +106 -0
- data/lib/moult/coverage/resolver.rb +56 -0
- data/lib/moult/coverage.rb +176 -0
- data/lib/moult/coverage_report.rb +98 -0
- data/lib/moult/dead_code.rb +119 -0
- data/lib/moult/dead_code_report.rb +65 -0
- data/lib/moult/diff.rb +177 -0
- data/lib/moult/discovery.rb +38 -0
- data/lib/moult/duplication/confidence.rb +92 -0
- data/lib/moult/duplication.rb +112 -0
- data/lib/moult/duplication_report.rb +89 -0
- data/lib/moult/flag_scanner.rb +150 -0
- data/lib/moult/flags/classification.rb +79 -0
- data/lib/moult/flags/snapshot.rb +162 -0
- data/lib/moult/flags/staleness.rb +145 -0
- data/lib/moult/flags.rb +131 -0
- data/lib/moult/flags_report.rb +136 -0
- data/lib/moult/formatters/boundaries_json.rb +20 -0
- data/lib/moult/formatters/boundaries_table.rb +53 -0
- data/lib/moult/formatters/coverage_json.rb +19 -0
- data/lib/moult/formatters/coverage_table.rb +60 -0
- data/lib/moult/formatters/dead_code_json.rb +20 -0
- data/lib/moult/formatters/dead_code_table.rb +66 -0
- data/lib/moult/formatters/duplication_json.rb +20 -0
- data/lib/moult/formatters/duplication_table.rb +55 -0
- data/lib/moult/formatters/flags_json.rb +20 -0
- data/lib/moult/formatters/flags_table.rb +76 -0
- data/lib/moult/formatters/gate_github.rb +52 -0
- data/lib/moult/formatters/gate_json.rb +20 -0
- data/lib/moult/formatters/gate_message.rb +19 -0
- data/lib/moult/formatters/gate_sarif.rb +78 -0
- data/lib/moult/formatters/gate_table.rb +71 -0
- data/lib/moult/formatters/health_json.rb +20 -0
- data/lib/moult/formatters/health_table.rb +80 -0
- data/lib/moult/formatters/json.rb +23 -0
- data/lib/moult/formatters/table.rb +70 -0
- data/lib/moult/formatters/text_table.rb +39 -0
- data/lib/moult/gate/config.rb +55 -0
- data/lib/moult/gate/evaluation.rb +172 -0
- data/lib/moult/gate/policy.rb +103 -0
- data/lib/moult/gate.rb +199 -0
- data/lib/moult/gate_report.rb +97 -0
- data/lib/moult/git.rb +83 -0
- data/lib/moult/health/score.rb +291 -0
- data/lib/moult/health.rb +320 -0
- data/lib/moult/health_report.rb +97 -0
- data/lib/moult/index.rb +228 -0
- data/lib/moult/parser.rb +101 -0
- data/lib/moult/rails_conventions.rb +124 -0
- data/lib/moult/report.rb +114 -0
- data/lib/moult/scoring.rb +82 -0
- data/lib/moult/span.rb +17 -0
- data/lib/moult/symbol_id.rb +30 -0
- data/lib/moult/symbol_scanner.rb +100 -0
- data/lib/moult/version.rb +5 -0
- data/lib/moult.rb +84 -0
- data/schema/boundaries.schema.json +125 -0
- data/schema/common.schema.json +76 -0
- data/schema/coverage.schema.json +83 -0
- data/schema/deadcode.schema.json +106 -0
- data/schema/duplication.schema.json +128 -0
- data/schema/flags.schema.json +157 -0
- data/schema/gate.schema.json +165 -0
- data/schema/health.schema.json +157 -0
- data/schema/hotspots.schema.json +106 -0
- metadata +185 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/moult-rb/moult-rb/blob/main/schema/deadcode.schema.json",
|
|
4
|
+
"title": "Moult dead-code report",
|
|
5
|
+
"description": "Typed output contract for `moult deadcode`. One of Moult's two protected APIs. Every finding is a confidence-graded candidate carrying its reasons; nothing here asserts that code is certainly dead.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "tool", "analysis", "findings"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"description": "Bumped only on a breaking change to this shape. v2 adds the runtime block (analysis.coverage provenance and a per-finding runtime field).",
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"const": 2
|
|
14
|
+
},
|
|
15
|
+
"tool": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["name", "version"],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"name": {"type": "string"},
|
|
21
|
+
"version": {"type": "string"}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"analysis": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["root", "git_ref", "generated_at", "coverage", "index"],
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"root": {"type": "string"},
|
|
30
|
+
"git_ref": {"type": ["string", "null"]},
|
|
31
|
+
"generated_at": {"type": ["string", "null"], "format": "date-time"},
|
|
32
|
+
"coverage": {
|
|
33
|
+
"description": "Provenance of merged runtime coverage (--coverage). Null when none was merged.",
|
|
34
|
+
"$ref": "common.schema.json#/$defs/coverage_source"
|
|
35
|
+
},
|
|
36
|
+
"index": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": ["backend", "backend_version", "resolved", "rails", "diagnostics"],
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"properties": {
|
|
41
|
+
"backend": {"type": "string"},
|
|
42
|
+
"backend_version": {"type": ["string", "null"]},
|
|
43
|
+
"resolved": {"type": "boolean"},
|
|
44
|
+
"rails": {"description": "Whether Rails entrypoint awareness was applied.", "type": "boolean"},
|
|
45
|
+
"diagnostics": {"type": "array", "items": {"type": "string"}}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"findings": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": {"$ref": "#/$defs/finding"}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"$defs": {
|
|
56
|
+
"reason": {
|
|
57
|
+
"description": "One auditable contribution to a finding's confidence.",
|
|
58
|
+
"type": "object",
|
|
59
|
+
"required": ["rule", "delta", "detail"],
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"properties": {
|
|
62
|
+
"rule": {"type": "string"},
|
|
63
|
+
"delta": {"description": "Signed adjustment this rule applied.", "type": "number"},
|
|
64
|
+
"detail": {"type": "string"}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"finding": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"required": ["symbol_id", "kind", "name", "span", "confidence", "category", "runtime", "reasons"],
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"properties": {
|
|
72
|
+
"symbol_id": {
|
|
73
|
+
"description": "Stable join key shared with the hotspots contract: \"<path>:<start_line>:<fqname>\".",
|
|
74
|
+
"type": "string"
|
|
75
|
+
},
|
|
76
|
+
"kind": {
|
|
77
|
+
"description": "What sort of definition this candidate is.",
|
|
78
|
+
"type": "string",
|
|
79
|
+
"enum": ["method", "constant"]
|
|
80
|
+
},
|
|
81
|
+
"name": {
|
|
82
|
+
"description": "Fully-qualified lexical name (Class#method or Class.method).",
|
|
83
|
+
"type": "string"
|
|
84
|
+
},
|
|
85
|
+
"span": {"$ref": "common.schema.json#/$defs/span"},
|
|
86
|
+
"confidence": {
|
|
87
|
+
"description": "Likelihood the candidate is actually dead, in [0, 1]. Never an assertion of certain death.",
|
|
88
|
+
"$ref": "common.schema.json#/$defs/unit_interval"
|
|
89
|
+
},
|
|
90
|
+
"category": {
|
|
91
|
+
"description": "Always \"dead_code\" in this contract.",
|
|
92
|
+
"type": "string"
|
|
93
|
+
},
|
|
94
|
+
"runtime": {
|
|
95
|
+
"description": "Runtime coverage classification merged from --coverage; null when no coverage was supplied. A runtime_cold/runtime_hot entry in reasons records its effect on the confidence.",
|
|
96
|
+
"$ref": "common.schema.json#/$defs/runtime"
|
|
97
|
+
},
|
|
98
|
+
"reasons": {
|
|
99
|
+
"description": "The factors that raised or lowered the confidence, in application order.",
|
|
100
|
+
"type": "array",
|
|
101
|
+
"items": {"$ref": "#/$defs/reason"}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/moult-rb/moult-rb/blob/main/schema/duplication.schema.json",
|
|
4
|
+
"title": "Moult duplication report",
|
|
5
|
+
"description": "Typed output contract for `moult duplication`. One of Moult's two protected APIs. Every finding is a confidence-graded clone group carrying its reasons; nothing here asserts that duplication is certainly removable.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "tool", "analysis", "summary", "findings"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"description": "Bumped only on a breaking change to this shape.",
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"const": 1
|
|
14
|
+
},
|
|
15
|
+
"tool": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["name", "version"],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"name": {"type": "string"},
|
|
21
|
+
"version": {"type": "string"}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"analysis": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["root", "git_ref", "generated_at", "detector"],
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"root": {"type": "string"},
|
|
30
|
+
"git_ref": {"type": ["string", "null"]},
|
|
31
|
+
"generated_at": {"type": ["string", "null"], "format": "date-time"},
|
|
32
|
+
"detector": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": ["backend", "backend_version", "min_mass", "fuzzy"],
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"properties": {
|
|
37
|
+
"backend": {"description": "Clone-detection backend, e.g. \"flay\".", "type": "string"},
|
|
38
|
+
"backend_version": {"type": ["string", "null"]},
|
|
39
|
+
"min_mass": {"description": "Mass threshold below which fragments were ignored.", "type": "integer", "minimum": 0},
|
|
40
|
+
"fuzzy": {"description": "Whether near-matches (not just structural-equivalents) were included.", "type": "boolean"}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"summary": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": ["sets", "occurrences", "total_mass"],
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"properties": {
|
|
50
|
+
"sets": {"description": "Number of clone groups.", "type": "integer", "minimum": 0},
|
|
51
|
+
"occurrences": {"description": "Total sites across all groups.", "type": "integer", "minimum": 0},
|
|
52
|
+
"total_mass": {"type": "integer", "minimum": 0}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"findings": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": {"$ref": "#/$defs/finding"}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"$defs": {
|
|
61
|
+
"reason": {
|
|
62
|
+
"description": "One auditable contribution to a finding's confidence.",
|
|
63
|
+
"type": "object",
|
|
64
|
+
"required": ["rule", "delta", "detail"],
|
|
65
|
+
"additionalProperties": false,
|
|
66
|
+
"properties": {
|
|
67
|
+
"rule": {"type": "string"},
|
|
68
|
+
"delta": {"description": "Signed adjustment this rule applied.", "type": "number"},
|
|
69
|
+
"detail": {"type": "string"}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"occurrence": {
|
|
73
|
+
"description": "One site of a clone group.",
|
|
74
|
+
"type": "object",
|
|
75
|
+
"required": ["symbol_id", "path", "line", "fuzzy"],
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"properties": {
|
|
78
|
+
"symbol_id": {
|
|
79
|
+
"description": "Best-effort enclosing-method join key (\"<path>:<start_line>:<fqname>\"), shared with the hotspots/deadcode contracts. Null when the fragment is not inside a known method (top-level code, or a duplicated whole class).",
|
|
80
|
+
"type": ["string", "null"]
|
|
81
|
+
},
|
|
82
|
+
"path": {"type": "string"},
|
|
83
|
+
"line": {"description": "Clone start line (flay reports line granularity).", "type": "integer", "minimum": 1},
|
|
84
|
+
"fuzzy": {"description": "True only for a near-match site surfaced in fuzzy mode.", "type": "boolean"}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"finding": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"required": ["category", "confidence", "kind", "node_type", "mass", "reasons", "occurrences"],
|
|
90
|
+
"additionalProperties": false,
|
|
91
|
+
"properties": {
|
|
92
|
+
"category": {
|
|
93
|
+
"description": "Always \"duplication\" in this contract.",
|
|
94
|
+
"type": "string"
|
|
95
|
+
},
|
|
96
|
+
"confidence": {
|
|
97
|
+
"description": "Likelihood this is genuine, consolidatable duplication, in [0, 1]. Never an assertion of certainty.",
|
|
98
|
+
"$ref": "common.schema.json#/$defs/unit_interval"
|
|
99
|
+
},
|
|
100
|
+
"kind": {
|
|
101
|
+
"description": "identical=byte-for-byte match; similar=same structure, differing names/literals.",
|
|
102
|
+
"type": "string",
|
|
103
|
+
"enum": ["identical", "similar"]
|
|
104
|
+
},
|
|
105
|
+
"node_type": {
|
|
106
|
+
"description": "The duplicated AST node's type (flay sexp type, e.g. \"defn\", \"call\", \"class\").",
|
|
107
|
+
"type": "string"
|
|
108
|
+
},
|
|
109
|
+
"mass": {
|
|
110
|
+
"description": "flay's structural mass of the duplicated node.",
|
|
111
|
+
"type": "integer",
|
|
112
|
+
"minimum": 0
|
|
113
|
+
},
|
|
114
|
+
"reasons": {
|
|
115
|
+
"description": "The factors that raised or lowered the confidence, in application order.",
|
|
116
|
+
"type": "array",
|
|
117
|
+
"items": {"$ref": "#/$defs/reason"}
|
|
118
|
+
},
|
|
119
|
+
"occurrences": {
|
|
120
|
+
"description": "The sites of this clone group; a group has at least two.",
|
|
121
|
+
"type": "array",
|
|
122
|
+
"minItems": 2,
|
|
123
|
+
"items": {"$ref": "#/$defs/occurrence"}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/moult-rb/moult-rb/blob/main/schema/flags.schema.json",
|
|
4
|
+
"title": "Moult feature-flags report",
|
|
5
|
+
"description": "Typed output contract for `moult flags`. One of Moult's two protected APIs. Each finding is one feature-flag key found by a static Prism scan of the OpenFeature client surface (provider-agnostic). Without --provider a flag reference is a recorded fact: confidence is null and the value_type/reference_count/default_values classification carries the signal (schema_version 1). With --provider a LOCAL provider snapshot (e.g. a flagd flag-definition export) is joined on the literal flag_key and each finding additionally carries a confidence-graded staleness candidate (status + confidence + reasons) and the report an analysis.provider provenance block (schema_version 2). The bump is additive: without a snapshot the output is byte-for-byte identical to v1. The snapshot is evidence (archived / fully rolled out / absent), never proof - nothing here asserts a flag is certainly stale, dead, or unused.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "tool", "analysis", "summary", "findings"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"description": "1 without a provider snapshot (usage only); 2 when --provider merges staleness candidates. The two share one additive shape: the v2-only blocks (analysis.provider, per-finding staleness, summary.by_staleness_status) are absent at v1.",
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"enum": [1, 2]
|
|
14
|
+
},
|
|
15
|
+
"tool": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["name", "version"],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"name": {"type": "string"},
|
|
21
|
+
"version": {"type": "string"}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"analysis": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["root", "git_ref", "generated_at", "scanner"],
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"root": {"type": "string"},
|
|
30
|
+
"git_ref": {"type": ["string", "null"]},
|
|
31
|
+
"generated_at": {"type": ["string", "null"], "format": "date-time"},
|
|
32
|
+
"provider": {
|
|
33
|
+
"description": "Provenance of a merged provider snapshot (--provider). Present only at schema_version 2; absent (the whole key omitted) without a snapshot.",
|
|
34
|
+
"$ref": "common.schema.json#/$defs/provider_source"
|
|
35
|
+
},
|
|
36
|
+
"scanner": {
|
|
37
|
+
"description": "Provenance of the static scan. This slice ingests no external tool output; it scans a client surface (the swap point for a future SDK/standard).",
|
|
38
|
+
"type": "object",
|
|
39
|
+
"required": ["target", "sdk_gem", "client_builder"],
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"properties": {
|
|
42
|
+
"target": {"description": "The flag standard scanned, e.g. \"openfeature\".", "type": "string"},
|
|
43
|
+
"sdk_gem": {"description": "The SDK gem whose call shape is detected (never a runtime dependency).", "type": "string"},
|
|
44
|
+
"client_builder": {"description": "The client constructor recognised, e.g. \"OpenFeature::SDK.build_client\".", "type": "string"}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"summary": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"required": ["flags", "references", "dynamic_references", "by_value_type"],
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
"properties": {
|
|
54
|
+
"flags": {"description": "Number of distinct literal flag keys catalogued.", "type": "integer", "minimum": 0},
|
|
55
|
+
"references": {"description": "Total catalogued reference sites across all flags.", "type": "integer", "minimum": 0},
|
|
56
|
+
"dynamic_references": {"description": "Flag-evaluation calls whose key was not a literal (counted, not catalogued; a static scan cannot resolve the key).", "type": "integer", "minimum": 0},
|
|
57
|
+
"by_value_type": {"description": "Reference count keyed by value_type.", "type": "object", "additionalProperties": {"type": "integer", "minimum": 0}},
|
|
58
|
+
"by_staleness_status": {"description": "Flag count keyed by staleness status. Present only at schema_version 2 (--provider).", "type": "object", "additionalProperties": {"type": "integer", "minimum": 0}}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"findings": {
|
|
62
|
+
"type": "array",
|
|
63
|
+
"items": {"$ref": "#/$defs/finding"}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"$defs": {
|
|
67
|
+
"reason": {
|
|
68
|
+
"description": "One auditable note behind a finding's classification.",
|
|
69
|
+
"type": "object",
|
|
70
|
+
"required": ["rule", "detail"],
|
|
71
|
+
"additionalProperties": false,
|
|
72
|
+
"properties": {
|
|
73
|
+
"rule": {"type": "string"},
|
|
74
|
+
"detail": {"type": "string"}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"occurrence": {
|
|
78
|
+
"description": "One reference site of a flag.",
|
|
79
|
+
"type": "object",
|
|
80
|
+
"required": ["symbol_id", "path", "line", "method"],
|
|
81
|
+
"additionalProperties": false,
|
|
82
|
+
"properties": {
|
|
83
|
+
"symbol_id": {
|
|
84
|
+
"description": "Best-effort enclosing-method join key (\"<path>:<start_line>:<fqname>\"), shared with the other contracts. Null for a top-level reference (outside any method).",
|
|
85
|
+
"type": ["string", "null"]
|
|
86
|
+
},
|
|
87
|
+
"path": {"description": "Root-relative referencing file path; the health files[] join key.", "type": "string"},
|
|
88
|
+
"line": {"description": "Call-site line (1-based).", "type": "integer", "minimum": 1},
|
|
89
|
+
"method": {"description": "The OpenFeature fetch method used at the site, e.g. \"fetch_boolean_value\" or \"fetch_string_details\". A plain string: the contract does not enumerate SDK method names.", "type": "string"}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"finding": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"required": ["category", "confidence", "flag_key", "value_type", "reference_count", "default_values", "reasons", "occurrences"],
|
|
95
|
+
"additionalProperties": false,
|
|
96
|
+
"properties": {
|
|
97
|
+
"category": {
|
|
98
|
+
"description": "Always \"feature_flag\" in this contract.",
|
|
99
|
+
"type": "string",
|
|
100
|
+
"const": "feature_flag"
|
|
101
|
+
},
|
|
102
|
+
"confidence": {
|
|
103
|
+
"description": "Null at schema_version 1 (a flag reference is a recorded fact, not a probabilistic candidate). At schema_version 2 it carries the staleness candidate's confidence (== staleness.confidence): the reserved per-finding confidence slot's first real use in this contract. Still a confidence in [0, 1], never a certainty.",
|
|
104
|
+
"$ref": "common.schema.json#/$defs/confidence"
|
|
105
|
+
},
|
|
106
|
+
"flag_key": {"description": "The literal flag key passed as flag_key:.", "type": "string"},
|
|
107
|
+
"value_type": {
|
|
108
|
+
"description": "The flag's value type, read from the fetch_<type>_* method. integer/float collapse to number; unknown means the flag was referenced with more than one type. A classification of recorded usage, never an assertion the flag is wrong.",
|
|
109
|
+
"type": "string",
|
|
110
|
+
"enum": ["boolean", "string", "number", "object", "unknown"]
|
|
111
|
+
},
|
|
112
|
+
"reference_count": {"description": "Number of catalogued reference sites (equals occurrences length).", "type": "integer", "minimum": 1},
|
|
113
|
+
"default_values": {
|
|
114
|
+
"description": "Distinct literal default values observed across the sites (string-rendered, sorted). Empty when no default was a literal.",
|
|
115
|
+
"type": "array",
|
|
116
|
+
"items": {"type": "string"}
|
|
117
|
+
},
|
|
118
|
+
"reasons": {
|
|
119
|
+
"description": "The notes behind the classification.",
|
|
120
|
+
"type": "array",
|
|
121
|
+
"items": {"$ref": "#/$defs/reason"}
|
|
122
|
+
},
|
|
123
|
+
"occurrences": {
|
|
124
|
+
"description": "The reference sites of this flag; at least one.",
|
|
125
|
+
"type": "array",
|
|
126
|
+
"minItems": 1,
|
|
127
|
+
"items": {"$ref": "#/$defs/occurrence"}
|
|
128
|
+
},
|
|
129
|
+
"staleness": {
|
|
130
|
+
"description": "A confidence-graded staleness candidate from the joined provider snapshot. Present only at schema_version 2 (--provider); the whole key is omitted without a snapshot. Evidence, never a claim of certain death.",
|
|
131
|
+
"$ref": "#/$defs/staleness"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"staleness": {
|
|
136
|
+
"description": "The staleness judgement for a flag, joined from the provider snapshot on flag_key. status records the observed lifecycle (archived/disabled/rolled_out/active) or absent (referenced in code, unknown to the provider); confidence grades how strong a removal candidate it is; reasons audit the call. active carries confidence 0.0 (not a removal candidate). Nothing here asserts certain death.",
|
|
137
|
+
"type": "object",
|
|
138
|
+
"required": ["status", "confidence", "reasons"],
|
|
139
|
+
"additionalProperties": false,
|
|
140
|
+
"properties": {
|
|
141
|
+
"status": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"enum": ["archived", "absent", "disabled", "rolled_out", "active"]
|
|
144
|
+
},
|
|
145
|
+
"confidence": {
|
|
146
|
+
"description": "Removal-candidate strength in [0, 1]. A confidence, never a certainty.",
|
|
147
|
+
"$ref": "common.schema.json#/$defs/unit_interval"
|
|
148
|
+
},
|
|
149
|
+
"reasons": {
|
|
150
|
+
"description": "The notes behind the staleness judgement.",
|
|
151
|
+
"type": "array",
|
|
152
|
+
"items": {"$ref": "#/$defs/reason"}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/moult-rb/moult-rb/blob/main/schema/gate.schema.json",
|
|
4
|
+
"title": "Moult PR risk gate report",
|
|
5
|
+
"description": "Typed output contract for `moult gate`, the diff-aware PR risk gate. This is the ONLY Moult contract that renders a verdict: it consumes the confidence-graded / classified SIGNALS the other analyses emit (none of which carry pass/fail), scopes them to the changed lines/files (Clean as You Code), and applies an explicit, recorded policy. The verdict is an auditable application of that policy over confidence-graded candidates -- never a claim that code is certainly wrong or dead. No signal contract carries a verdict; it lives only here.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "tool", "analysis", "policy", "verdict", "reasons", "summary", "rules"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"description": "Bumped only on a breaking change to this shape.",
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"const": 1
|
|
14
|
+
},
|
|
15
|
+
"tool": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["name", "version"],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"name": {"type": "string"},
|
|
21
|
+
"version": {"type": "string"}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"analysis": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["root", "git_ref", "generated_at", "base_ref", "merge_base", "scope", "components"],
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"root": {"type": "string"},
|
|
30
|
+
"git_ref": {"type": ["string", "null"]},
|
|
31
|
+
"generated_at": {"type": ["string", "null"], "format": "date-time"},
|
|
32
|
+
"base_ref": {
|
|
33
|
+
"description": "The requested base ref the diff is taken against (e.g. \"origin/main\"). Null under scope \"all\".",
|
|
34
|
+
"type": ["string", "null"]
|
|
35
|
+
},
|
|
36
|
+
"merge_base": {
|
|
37
|
+
"description": "The resolved merge-base sha between base_ref and HEAD -- the \"new code\" boundary. Null under scope \"all\".",
|
|
38
|
+
"type": ["string", "null"]
|
|
39
|
+
},
|
|
40
|
+
"scope": {
|
|
41
|
+
"description": "\"diff\" gates only findings intersecting the changed lines/files; \"all\" gates the whole codebase.",
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": ["diff", "all"]
|
|
44
|
+
},
|
|
45
|
+
"components": {
|
|
46
|
+
"description": "Provenance: which signal analyses the gate composed, and which were skipped (errored or not applicable). Mirrors the health composer discipline.",
|
|
47
|
+
"type": "array",
|
|
48
|
+
"items": {"$ref": "#/$defs/component"}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"policy": {
|
|
53
|
+
"description": "Every threshold that was applied, recorded in full so the verdict is reproducible and auditable.",
|
|
54
|
+
"type": "object",
|
|
55
|
+
"required": ["source", "dead_code_max_confidence", "boundary_max_severity", "complexity_ceiling", "duplication_max_mass", "exclude_paths"],
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"properties": {
|
|
58
|
+
"source": {"description": "\"default\" or the config file the overrides came from.", "type": "string"},
|
|
59
|
+
"dead_code_max_confidence": {"$ref": "common.schema.json#/$defs/unit_interval"},
|
|
60
|
+
"boundary_max_severity": {"type": "string", "enum": ["low", "medium", "high"]},
|
|
61
|
+
"complexity_ceiling": {"type": "number", "minimum": 0},
|
|
62
|
+
"duplication_max_mass": {"type": "number", "minimum": 0},
|
|
63
|
+
"exclude_paths": {
|
|
64
|
+
"description": "Root-relative path prefixes dropped from gating (e.g. test/spec). The gate judges production code; override to [] to gate everything.",
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": {"type": "string"}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"verdict": {
|
|
71
|
+
"description": "The single top-level outcome. The only pass/fail in any Moult contract.",
|
|
72
|
+
"type": "string",
|
|
73
|
+
"enum": ["pass", "fail"]
|
|
74
|
+
},
|
|
75
|
+
"reasons": {
|
|
76
|
+
"description": "Why the verdict came out the way it did.",
|
|
77
|
+
"type": "array",
|
|
78
|
+
"items": {"$ref": "#/$defs/reason"}
|
|
79
|
+
},
|
|
80
|
+
"summary": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"required": ["rules", "evaluated", "failed", "findings"],
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"properties": {
|
|
85
|
+
"rules": {"description": "Total policy rules.", "type": "integer", "minimum": 0},
|
|
86
|
+
"evaluated": {"description": "Rules whose backing analysis ran.", "type": "integer", "minimum": 0},
|
|
87
|
+
"failed": {"description": "Evaluated rules that the changes violated.", "type": "integer", "minimum": 0},
|
|
88
|
+
"findings": {"description": "Total contributing findings across failed rules.", "type": "integer", "minimum": 0}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"rules": {
|
|
92
|
+
"type": "array",
|
|
93
|
+
"items": {"$ref": "#/$defs/rule"}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"$defs": {
|
|
97
|
+
"reason": {
|
|
98
|
+
"description": "One auditable note behind the verdict or a rule outcome.",
|
|
99
|
+
"type": "object",
|
|
100
|
+
"required": ["rule", "detail"],
|
|
101
|
+
"additionalProperties": false,
|
|
102
|
+
"properties": {
|
|
103
|
+
"rule": {"type": "string"},
|
|
104
|
+
"detail": {"type": "string"}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"component": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"required": ["name", "present", "diagnostic"],
|
|
110
|
+
"additionalProperties": false,
|
|
111
|
+
"properties": {
|
|
112
|
+
"name": {"type": "string"},
|
|
113
|
+
"present": {"description": "Whether this analysis contributed observations.", "type": "boolean"},
|
|
114
|
+
"diagnostic": {"description": "Why it was skipped, when absent; null when present.", "type": ["string", "null"]}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"contribution": {
|
|
118
|
+
"description": "One finding that contributed to a rule outcome. Stays a confidence-graded / classified candidate: `value` is the observed signal, never an assertion of certainty.",
|
|
119
|
+
"type": "object",
|
|
120
|
+
"required": ["category", "path", "symbol_id", "line", "value"],
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"properties": {
|
|
123
|
+
"category": {"$ref": "common.schema.json#/$defs/category"},
|
|
124
|
+
"path": {"description": "Root-relative file path.", "type": "string"},
|
|
125
|
+
"symbol_id": {
|
|
126
|
+
"description": "Method-level join key (\"<path>:<start_line>:<fqname>\"), shared with the other contracts. Null where the finding is file-keyed (boundaries) or outside any method.",
|
|
127
|
+
"type": ["string", "null"]
|
|
128
|
+
},
|
|
129
|
+
"line": {"description": "1-based line of the finding, when known; null for file-keyed findings.", "type": ["integer", "null"], "minimum": 1},
|
|
130
|
+
"value": {
|
|
131
|
+
"description": "The observed signal that tripped the rule (a confidence, an ABC score, a clone mass, or a severity). A graded/classified value, never a certainty.",
|
|
132
|
+
"type": ["number", "string", "null"]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"rule": {
|
|
137
|
+
"description": "One policy rule's outcome over the scoped changes.",
|
|
138
|
+
"type": "object",
|
|
139
|
+
"required": ["rule", "evaluated", "observed", "threshold", "passed", "reasons", "findings"],
|
|
140
|
+
"additionalProperties": false,
|
|
141
|
+
"properties": {
|
|
142
|
+
"rule": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"enum": ["no_new_dead_code", "no_new_high_severity_boundary", "new_code_complexity_ceiling", "new_code_duplication_ceiling"]
|
|
145
|
+
},
|
|
146
|
+
"evaluated": {"description": "False when the backing analysis didn't run; such a rule never fails the gate.", "type": "boolean"},
|
|
147
|
+
"observed": {
|
|
148
|
+
"description": "The worst observed value among scoped findings for this rule (max confidence/ABC/mass, or highest severity). Null when not evaluated or nothing was observed.",
|
|
149
|
+
"type": ["number", "string", "null"]
|
|
150
|
+
},
|
|
151
|
+
"threshold": {"description": "The applied threshold, rendered as a comparison (e.g. \"< 0.8\", \"<= medium\").", "type": "string"},
|
|
152
|
+
"passed": {"description": "Whether the changes satisfied the rule. Null when not evaluated.", "type": ["boolean", "null"]},
|
|
153
|
+
"reasons": {
|
|
154
|
+
"type": "array",
|
|
155
|
+
"items": {"$ref": "#/$defs/reason"}
|
|
156
|
+
},
|
|
157
|
+
"findings": {
|
|
158
|
+
"description": "The contributing findings (empty when the rule passed or was skipped).",
|
|
159
|
+
"type": "array",
|
|
160
|
+
"items": {"$ref": "#/$defs/contribution"}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|