filegrc 0.16.23 → 0.16.24

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "filegrc",
3
- "version": "0.16.23",
3
+ "version": "0.16.24",
4
4
  "description": "Zero-dependency Git-native GRC engine",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -3,6 +3,7 @@ import { modelSupports } from "../model/index.js";
3
3
  import {
4
4
  collectionRevision,
5
5
  collectionReviewDecisionMatches,
6
+ controlReviewerConflictIds,
6
7
  reviewedControlConflictIds
7
8
  } from "./collection-revision.js";
8
9
  import { collectionRecordProposals, retentionScheduleReviewScope, scopedCollectionRecords } from "./collection-scope.js";
@@ -450,19 +451,7 @@ async function requireRetentionScheduleReady(loaded, resourceType, program, rete
450
451
  }
451
452
 
452
453
  function assessControlReviewers(loaded, controls) {
453
- const byId = new Map(loaded.resources.map((record) => [record.id, record]));
454
- const controlIds = new Set(controls.map(({ id }) => id));
455
- const conflictIds = new Set();
456
- for (const control of controls) {
457
- for (const id of currentPartyPeople(control.ownerIds || [], byId)) conflictIds.add(id);
458
- }
459
- for (const obligation of loaded.resources.filter((record) => (
460
- record.type === "obligation"
461
- && record.status === "active"
462
- && (record.controlIds || []).some((id) => controlIds.has(id))
463
- ))) {
464
- for (const id of currentPartyPeople(obligation.ownerIds || [], byId)) conflictIds.add(id);
465
- }
454
+ const conflictIds = new Set(controlReviewerConflictIds(loaded, controls));
466
455
  const people = loaded.resources.filter(({ type, status }) => type === "person" && status === "active");
467
456
  return {
468
457
  eligibleReviewerIds: people.map(({ id }) => id).filter((id) => !conflictIds.has(id)),
@@ -128,14 +128,21 @@ export function reviewedControlConflictIds(loaded, review) {
128
128
  const controls = scopedCollectionRecords(source, "control", program);
129
129
  const controlIds = new Set(controls.map(({ id }) => id));
130
130
  const byId = new Map(source.resources.map((record) => [record.id, record]));
131
+ return controlReviewerConflictIds(source, controls, byId, controlIds);
132
+ }
133
+
134
+ export function controlReviewerConflictIds(loaded, controls, byId = new Map(loaded.resources.map((record) => [record.id, record])), controlIds = new Set(controls.map(({ id }) => id))) {
131
135
  const conflicts = new Set();
132
- for (const record of [
133
- ...controls,
134
- ...source.resources.filter((record) => record.type === "obligation"
135
- && record.status === "active"
136
- && (record.controlIds || []).some((id) => controlIds.has(id)))
137
- ]) {
138
- for (const id of currentPartyPeople(record.ownerIds || [], byId)) conflicts.add(id);
136
+ for (const control of controls) {
137
+ for (const id of currentPartyPeople(control.ownerIds || [], byId)) conflicts.add(id);
138
+ }
139
+ for (const obligation of loaded.resources.filter((record) => record.type === "obligation"
140
+ && record.status === "active"
141
+ && (record.controlIds || []).some((id) => controlIds.has(id)))) {
142
+ // A team owns the work queue, but membership alone does not mean each member
143
+ // performed its work. Named people and appointment holders remain conflicts.
144
+ const namedOwners = (obligation.ownerIds || []).filter((id) => byId.get(id)?.type !== "team");
145
+ for (const id of currentPartyPeople(namedOwners, byId)) conflicts.add(id);
139
146
  }
140
147
  return [...conflicts].sort();
141
148
  }
package/src/web.js CHANGED
@@ -650,6 +650,9 @@ function renderStageOverview(main, stageId, params = new URLSearchParams()) {
650
650
  });
651
651
  main.querySelector("[data-review-policy-activation]")?.addEventListener("click", openPolicyActivationDialog);
652
652
  main.querySelector("[data-review-document-activation]")?.addEventListener("click", () => openDocumentActivationDialog());
653
+ if (stage.id === "controls") {
654
+ main.querySelector("[data-review-collection]")?.addEventListener("click", () => openCollectionReviewDialog("control"));
655
+ }
653
656
  main.querySelector("[data-review-audit-document-activation]")?.addEventListener("click", (event) => {
654
657
  openDocumentActivationDialog(event.currentTarget.dataset.reviewAuditDocumentActivation);
655
658
  });