Skip to content

Commit 58487bd

Browse files
authored
added force definitions (#29)
1 parent bb56153 commit 58487bd

7 files changed

Lines changed: 379 additions & 421 deletions

File tree

‎fedramp-consolidated-rules.json‎

Lines changed: 228 additions & 420 deletions
Large diffs are not rendered by default.

‎schemas/fedramp-consolidated-rules.schema.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@
483483
"tag": { "type": "string" },
484484
"alts": { "type": "array", "items": { "type": "string" } },
485485
"do_not_link": { "type": "boolean" },
486+
"ignore_in_terms": { "type": "boolean" },
486487
"reference": { "type": "string" },
487488
"reference_url": { "type": "string", "format": "uri" },
488489
"referenceurl": { "type": "string", "format": "uri" },

‎tools/README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ normalizations. Focused aliases select individual scopes:
112112
| `bun run fix:display-names` | Repairs inline rule IDs and their parenthesized display names. |
113113
| `bun run fix:subset-affects` | Aligns FRR subset applicability `affects` with its requirements. |
114114

115+
Term synchronization excludes definitions with `ignore_in_terms: true`,
116+
including their aliases, from FRR and KSI `terms` arrays. The term check reports
117+
existing entries for these definitions, and the fixer removes them. An absent
118+
or `false` flag keeps normal matching behavior.
119+
115120
Pass flags after `--`:
116121

117122
| Flag | Behavior |

‎tools/src/terms.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ function buildTermLookup(document: RulesDocument): Map<string, string> {
5151
const lookup = new Map<string, string>();
5252

5353
for (const { definition } of getDefinitionEntries(document)) {
54+
if (definition.ignore_in_terms === true) {
55+
continue;
56+
}
57+
5458
lookup.set(definition.term.toLowerCase(), definition.term);
5559

5660
for (const alt of definition.alts ?? []) {

‎tools/src/types.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface UpdatedEntry {
1111
export interface DefinitionEntry {
1212
term: string;
1313
alts?: string[];
14+
ignore_in_terms?: boolean;
1415
tag?: string;
1516
definition: string;
1617
note?: string;

‎tools/tests/fix.test.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ test("auto-fix applies ID, term, related, subset-affects, and property-order fix
103103
properties: {
104104
term: { type: "string" },
105105
alts: { type: "array" },
106+
ignore_in_terms: { type: "boolean" },
106107
definition: { type: "string" },
107108
updated: { type: "array" },
108109
},
@@ -152,6 +153,12 @@ test("auto-fix applies ID, term, related, subset-affects, and property-order fix
152153
definition: "Test definition",
153154
updated: [],
154155
},
156+
"FRD-MST": {
157+
term: "MUST",
158+
ignore_in_terms: true,
159+
definition: "Test definition",
160+
updated: [],
161+
},
155162
},
156163
},
157164
},
@@ -181,7 +188,7 @@ test("auto-fix applies ID, term, related, subset-affects, and property-order fix
181188
related: ["MAS-CSO-OLD"],
182189
affects: ["Providers"],
183190
name: "Test requirement",
184-
terms: [],
191+
terms: ["MUST"],
185192
updated: [],
186193
},
187194
"MAS-CSO-REF": {

‎tools/tests/terms.test.ts‎

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,141 @@ import {
88
collectDefinitionTermTitleChanges,
99
collectTermSyncChanges,
1010
toDefaultTitleCase,
11+
type TermSyncChange,
1112
} from "../src/terms";
1213
import type { RulesDocument } from "../src/types";
1314

15+
function createTermSyncDocument(ignoreInTerms?: boolean): RulesDocument {
16+
return {
17+
info: {
18+
title: "Test",
19+
description: "Test",
20+
version: "1.0.0",
21+
last_updated: "2026-09-13",
22+
},
23+
FRD: {
24+
info: {},
25+
data: {
26+
all: {
27+
"FRD-MST": {
28+
term: "MUST",
29+
alts: ["required"],
30+
...(ignoreInTerms === undefined
31+
? {}
32+
: { ignore_in_terms: ignoreInTerms }),
33+
definition: "Test definition",
34+
},
35+
"FRD-AGY": {
36+
term: "Agency",
37+
definition: "Test definition",
38+
},
39+
"FRD-PVD": {
40+
term: "Provider",
41+
alts: ["providers"],
42+
ignore_in_terms: false,
43+
definition: "Test definition",
44+
},
45+
},
46+
},
47+
},
48+
FRR: {
49+
MAS: {
50+
info: {},
51+
data: {
52+
all: {
53+
CSO: {
54+
"MAS-CSO-TST": {
55+
name: "Test requirement",
56+
affects: ["Providers"],
57+
statement: "Providers MUST notify an agency.",
58+
force: "MUST",
59+
terms: [],
60+
},
61+
},
62+
},
63+
},
64+
},
65+
},
66+
KSI: {
67+
IAM: {
68+
id: "KSI-IAM",
69+
name: "Test theme",
70+
web_name: "Test theme",
71+
short_name: "Test",
72+
theme: "Test",
73+
indicators: {
74+
"KSI-IAM-TST": {
75+
name: "Test indicator",
76+
varies_by_class: {
77+
b: { statement: "Providers are required to notify an agency." },
78+
c: { statement: "Providers are required to notify an agency." },
79+
},
80+
terms: [],
81+
},
82+
},
83+
},
84+
},
85+
};
86+
}
87+
88+
test.each([
89+
["absent", undefined],
90+
["false", false],
91+
["true", true],
92+
] as const)("term sync honors ignore_in_terms=%s for terms and aliases", (_, ignoreInTerms) => {
93+
const document = createTermSyncDocument(ignoreInTerms);
94+
const expectedTerms = ignoreInTerms === true
95+
? ["Agency", "Provider"]
96+
: ["Agency", "MUST", "Provider"];
97+
const expectedChanges: TermSyncChange[] = [
98+
{
99+
id: "MAS-CSO-TST",
100+
location: "FRR.MAS.data.all.CSO.MAS-CSO-TST",
101+
kind: "requirement",
102+
currentTerms: [],
103+
nextTerms: expectedTerms,
104+
},
105+
{
106+
id: "KSI-IAM-TST",
107+
location: "KSI.IAM.indicators.KSI-IAM-TST",
108+
kind: "indicator",
109+
currentTerms: [],
110+
nextTerms: expectedTerms,
111+
},
112+
];
113+
const original = structuredClone(document);
114+
115+
expect(collectTermSyncChanges(document)).toEqual(expectedChanges);
116+
expect(document).toEqual(original);
117+
expect(applyTermSync(document)).toEqual(expectedChanges);
118+
expect(collectTermSyncChanges(document)).toEqual([]);
119+
expect(applyTermSync(document)).toEqual([]);
120+
});
121+
122+
test.each(["all", "20x", "rev5"])("term sync removes ignored definitions from %s even when their text still matches", (scope) => {
123+
const document = createTermSyncDocument(true);
124+
const definitions = document.FRD.data.all!;
125+
document.FRD.data = { [scope]: definitions };
126+
const requirement = document.FRR.MAS!.data.all!.CSO!["MAS-CSO-TST"]!;
127+
const indicator = Object.values(document.KSI.IAM!.indicators)[0]!;
128+
requirement.terms = ["Agency", "MUST", "Provider"];
129+
indicator.varies_by_class = {
130+
b: { statement: "This behavior is required." },
131+
c: { statement: "This behavior is required." },
132+
};
133+
indicator.terms = ["MUST"];
134+
135+
const changes = collectTermSyncChanges(document);
136+
137+
expect(changes).toHaveLength(2);
138+
expect(changes[0]!.nextTerms).toEqual(["Agency", "Provider"]);
139+
expect(changes[1]!.nextTerms).toEqual([]);
140+
expect(applyTermSync(document)).toEqual(changes);
141+
expect(requirement.terms).toEqual(["Agency", "Provider"]);
142+
expect(indicator.terms).toEqual([]);
143+
expect(collectTermSyncChanges(document)).toEqual([]);
144+
});
145+
14146
test("all FRD terms use the default title casing", () => {
15147
const changes = collectDefinitionTermTitleChanges(loadRulesDocument());
16148
expect(changes).toEqual([]);

0 commit comments

Comments
 (0)