' + esc(titleCase(item.title)) + '
' + esc(windowText(item)) + '
' + population + (item.timingIssue ? '' + esc(item.timingIssue) + '
' : '') + 'diff --git a/package-lock.json b/package-lock.json index 375ef1a..5f6a4d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "filegrc-monorepo", - "version": "0.21.1", + "version": "0.21.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "filegrc-monorepo", - "version": "0.21.1", + "version": "0.21.2", "workspaces": [ "packages/filegrc", "packages/create-filegrc" @@ -24,7 +24,7 @@ "link": true }, "packages/create-filegrc": { - "version": "0.21.1", + "version": "0.21.2", "license": "MIT", "bin": { "create-filegrc": "bin/create-filegrc.js" @@ -34,7 +34,7 @@ } }, "packages/filegrc": { - "version": "0.21.1", + "version": "0.21.2", "license": "MIT", "bin": { "filegrc": "bin/filegrc.js" diff --git a/package.json b/package.json index e8e6248..ed3fd52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "filegrc-monorepo", - "version": "0.21.1", + "version": "0.21.2", "private": true, "workspaces": [ "packages/filegrc", diff --git a/packages/create-filegrc/package.json b/packages/create-filegrc/package.json index 78cb11c..b10f646 100644 --- a/packages/create-filegrc/package.json +++ b/packages/create-filegrc/package.json @@ -1,6 +1,6 @@ { "name": "create-filegrc", - "version": "0.21.1", + "version": "0.21.2", "description": "Create a filegrc workspace for a SOC 2 program", "license": "MIT", "repository": { diff --git a/packages/create-filegrc/src/index.js b/packages/create-filegrc/src/index.js index 675ca1e..11c776c 100644 --- a/packages/create-filegrc/src/index.js +++ b/packages/create-filegrc/src/index.js @@ -524,13 +524,13 @@ async function runCombinedSetup(target, input) { async function writeMinimalLockfile(target, name, versionRange) { const lock = { name, - version: "0.21.1", + version: "0.21.2", lockfileVersion: 3, requires: true, packages: { "": { name, - version: "0.21.1", + version: "0.21.2", dependencies: { filegrc: versionRange } } } diff --git a/packages/create-filegrc/template/package.json b/packages/create-filegrc/template/package.json index b81de32..523d131 100644 --- a/packages/create-filegrc/template/package.json +++ b/packages/create-filegrc/template/package.json @@ -1,6 +1,6 @@ { "name": "{{project_name}}", - "version": "0.21.1", + "version": "0.21.2", "private": true, "description": "filegrc workspace for a SOC 2 program", "type": "module", diff --git a/packages/filegrc/package.json b/packages/filegrc/package.json index f62dca2..f58115d 100644 --- a/packages/filegrc/package.json +++ b/packages/filegrc/package.json @@ -1,6 +1,6 @@ { "name": "filegrc", - "version": "0.21.1", + "version": "0.21.2", "description": "Zero-dependency Git-native GRC engine", "license": "MIT", "repository": { diff --git a/packages/filegrc/src/schedule-set.js b/packages/filegrc/src/schedule-set.js index e092730..72eb8f4 100644 --- a/packages/filegrc/src/schedule-set.js +++ b/packages/filegrc/src/schedule-set.js @@ -84,7 +84,11 @@ export function scheduleSetIssues(loaded, program, review, options = {}) { const rule = options.at ? obligationRule(obligation, byId, { now: options.at }) : byId.get(obligation.activeRuleId); if (obligation.status !== "active" || (obligation.scheduleMode === "rule" && (!rule || rule.obligationId !== obligation.id || !["active", "retired"].includes(rule.status) || decision.ruleId !== obligation.activeRuleId || !reviewedRuleChainContains(obligation, decision.ruleId, rule.id, byId) || !rule.approvedByIds?.length || !rule.approvedOn))) { - add(obligation.id, `${obligation.title} needs its adopted, approved rule effective at cutover.`); + add(obligation.id, decision.ruleId !== obligation.activeRuleId && obligation.scheduleMode === "rule" + ? `${obligation.title}: the schedule review must name the currently adopted rule. Independently review the current Program schedule set.` + : options.at + ? `${obligation.title} needs its adopted, approved rule effective at cutover.` + : `${obligation.title} needs its currently adopted, approved rule. Review the current Program schedule set.`); } else if (rule && (rule.approvedOn > currentCalendarDate(rule.timezone || loaded.workspace?.timezone || "UTC") || !Array.isArray(rule.approvedByIds) || rule.approvedByIds.some((id) => !personWasActiveOn(byId.get(id), rule.approvedOn)))) { add(obligation.id, `${obligation.title} needs an actual rule approval by People active on its approval date.`); diff --git a/packages/filegrc/src/web.js b/packages/filegrc/src/web.js index a48553b..13769cb 100644 --- a/packages/filegrc/src/web.js +++ b/packages/filegrc/src/web.js @@ -2544,7 +2544,7 @@ function obligationCard(item, collapsed = false) { : ""); const kind = item.kind === "event" ? "Policy Event Task" : item.kind === "action" ? "Assigned Follow-up" : properCase(item.activityType || "Recurring"); const population = item.ruleId - ? '
' + esc(String(item.completedCount || 0)) + ' of ' + esc(String(item.expectedCount || 0)) + ' expected members passed' + (item.awaitingPeriodEnd ? '; work recorded, awaiting full period end ' + esc(item.requiredCoverage.endsOn) : item.membershipFinal ? '' : (item.awaitingPopulationCutoff ? '; work recorded, awaiting population cutoff ' : '; population remains open through ') + esc(item.membershipCutoffAt)) + '.
' + ? '' + esc(String(item.completedCount || 0)) + ' of ' + esc(String(item.expectedCount || 0)) + ' expected members passed' + (item.awaitingPeriodEnd ? item.requiredCoverage?.endsOn ? '; work recorded, awaiting full period end ' + esc(item.requiredCoverage.endsOn) : '; review the required period end before reconciliation' : item.membershipFinal ? '' : item.membershipCutoffAt ? (item.awaitingPopulationCutoff ? '; work recorded, awaiting population cutoff ' : '; population remains open through ') + esc(item.membershipCutoffAt) : '; record the actual population observation date with the work') + '.
' : ''; return '' + esc(windowText(item)) + '
' + population + (item.timingIssue ? '' + esc(item.timingIssue) + '
' : '') + 'The full period remains open through ' + esc(record.requiredCoverage?.endsOn) + '. Reconcile after its inclusive final day, even for zero or non-applicable populations.
' + ? record.requiredCoverage?.endsOn + ? 'The full period remains open through ' + esc(record.requiredCoverage.endsOn) + '. Reconcile after its inclusive final day, even for zero or non-applicable populations.
' + : 'Review the required period end before reconciling this occurrence.
' : membershipFinal ? "" - : 'Population remains open through ' + esc(record.membershipCutoffAt) + '. Review now, then reconcile after the cutoff.
'; + : record.membershipCutoffAt + ? 'Population remains open through ' + esc(record.membershipCutoffAt) + '. Review now, then reconcile after the cutoff.
' + : 'Record the date you actually observed this population. Reconcile after reviewing its members and proof.
'; if (!members.length) { return 'Confirm the zero population, then set the record to reconciled.