create-filegrc 0.1.0 → 0.3.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.
Files changed (48) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +5 -3
  3. package/package.json +3 -3
  4. package/src/cli.js +30 -12
  5. package/src/defaults.js +6 -7
  6. package/src/index.js +88 -22
  7. package/template/AGENTS.md +63 -23
  8. package/template/README.md +49 -24
  9. package/template/WORKSPACE.md +41 -0
  10. package/template/data/AGENTS.md +12 -10
  11. package/template/data/action-items/AGENTS.md +2 -2
  12. package/template/data/audits/AGENTS.md +13 -2
  13. package/template/data/documents/document-business-continuity-disaster-recovery.json +0 -1
  14. package/template/data/documents/document-contractor-policy-acknowledgement.json +0 -1
  15. package/template/data/documents/document-contractor-training-acknowledgement.json +0 -1
  16. package/template/data/documents/document-data-retention-schedule.json +0 -1
  17. package/template/data/documents/document-employee-handbook-acknowledgement.json +0 -1
  18. package/template/data/documents/document-employee-policy-acknowledgement.json +0 -1
  19. package/template/data/documents/document-employee-training-acknowledgement.json +0 -1
  20. package/template/data/documents/document-incident-response-plan.json +0 -1
  21. package/template/data/documents/document-soc2-management-assertion.json +0 -3
  22. package/template/data/documents/document-soc2-management-representation.json +0 -3
  23. package/template/data/documents/document-soc2-period-completeness.json +0 -3
  24. package/template/data/documents/document-soc2-period-completeness.md +2 -2
  25. package/template/data/documents/document-soc2-system-description.json +0 -3
  26. package/template/data/documents/document-soc2-system-description.md +3 -3
  27. package/template/data/evidence/AGENTS.md +5 -3
  28. package/template/data/obligation-events/AGENTS.md +3 -3
  29. package/template/data/obligations/AGENTS.md +2 -1
  30. package/template/data/people/person-policy-owner.json +1 -1
  31. package/template/data/policies/AGENTS.md +3 -1
  32. package/template/data/policies/policy-anti-bribery-corruption.json +0 -1
  33. package/template/data/policies/policy-clear-desk-screen.json +0 -1
  34. package/template/data/policies/policy-data-protection-handling.json +0 -1
  35. package/template/data/policies/policy-employee-handbook.json +0 -1
  36. package/template/data/policies/policy-information-security.json +0 -1
  37. package/template/data/policies/policy-information-security.md +3 -3
  38. package/template/data/policies/policy-mobile-computing-communications.json +0 -1
  39. package/template/data/renderer.json +2 -1
  40. package/template/data/risk-assessments/AGENTS.md +1 -1
  41. package/template/data/systems/system-filegrc-program-repository.md +3 -3
  42. package/template/data/workspace.json +1 -1
  43. package/template/docs/filegrc-audit.png +0 -0
  44. package/template/docs/filegrc-home.png +0 -0
  45. package/template/docs/filegrc-social-preview.png +0 -0
  46. package/template/package.json +2 -2
  47. package/template-parameters.json +18 -2
  48. package/template/data/people/person-independent-approver.json +0 -10
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 FileGRC contributors
3
+ Copyright (c) 2026 filegrc contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # create-filegrc
2
2
 
3
- Create a Git-native FileGRC workspace for a SOC 2 program:
3
+ Create a Git-native filegrc workspace for a SOC 2 program:
4
4
 
5
5
  ```sh
6
6
  npx create-filegrc@latest company-grc
@@ -9,10 +9,12 @@ npm run validate
9
9
  npm run serve
10
10
  ```
11
11
 
12
- Setup asks for the company name, the initial policy owner, and a security contact email. The generated private project includes a starter Security program, policy-driven work queue, event checklists, audit preparation, evidence packets, and one dependency: `filegrc`.
12
+ Setup asks for the legal organization name, the initial policy owner and their email, a security reporting address, and the program timezone. The generated private project includes a starter Security program, Program Readiness, a policy-driven Work Queue, Policy Events that add linked tasks, later audit preparation, evidence packets, and one dependency: `filegrc`.
13
13
 
14
- Generated workspaces also include layered `AGENTS.md` instructions and model-driven headless commands. Agents can discover types, scaffold JSON plus Markdown, inspect relationships, perform CRUD, complete policy work, and prepare audit packets through the same domain functions used by the renderer.
14
+ Generated workspaces also include layered `AGENTS.md` instructions and model-driven headless commands. `filegrc program-path` gives agents the same six steps, exact page guidance, current status, and next actions shown in the renderer. Agents can define program scope, approve policies, implement controls, test External Evidence, complete policy work, trigger event tasks, and prepare later audit packets through the same domain functions used by the renderer.
15
15
 
16
16
  Git is initialized when needed. The browser can create local commits before a remote is configured.
17
17
 
18
+ Creation output reports the resolved filegrc version, program timezone, starter record counts, whether installation ran, and whether the target joined an existing Git worktree or received a new repository.
19
+
18
20
  Use `npx create-filegrc@latest --help` for non-interactive options.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "create-filegrc",
3
- "version": "0.1.0",
4
- "description": "Create a FileGRC workspace for a SOC 2 program",
3
+ "version": "0.3.0",
4
+ "description": "Create a filegrc workspace for a SOC 2 program",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/Sunpeak-AI/FileGRC.git"
8
+ "url": "git+https://github.com/Sunpeak-AI/filegrc.git"
9
9
  },
10
10
  "type": "module",
11
11
  "bin": {
package/src/cli.js CHANGED
@@ -1,21 +1,35 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { createFileGRC } from "./index.js";
2
+ import { createFilegrc } from "./index.js";
3
3
 
4
4
  export async function runCli(argv = process.argv.slice(2)) {
5
5
  const { target, options } = parseArgs(argv);
6
6
  if (options.help) return printHelp();
7
7
  if (options.version) return printVersion();
8
- const result = await createFileGRC({ target, ...options });
8
+ const result = await createFilegrc({ target, ...options });
9
9
  console.log(`Created ${result.target}`);
10
+ console.log(`filegrc ${result.engineVersion}: ${result.install === "installed" ? "installed" : "installation skipped"}`);
11
+ console.log(`Git: ${result.gitMode === "existing-worktree" ? "joined existing worktree" : "initialized new repository"}`);
12
+ console.log(`Timezone: ${result.values.timezone}`);
13
+ console.log(
14
+ `Program baseline: ${result.resourceCounts.total} records, including ` +
15
+ `${result.resourceCounts.byType.requirement || 0} requirements, ` +
16
+ `${result.resourceCounts.byType.control || 0} controls, and ` +
17
+ `${result.resourceCounts.byType.obligation || 0} obligations.`
18
+ );
10
19
  console.log("");
11
- console.log(` cd ${result.target}`);
20
+ console.log(` cd ${shellQuote(result.target)}`);
12
21
  if (options.install === false) console.log(" npm install");
22
+ console.log(" npx filegrc setup");
13
23
  console.log(" npm run validate");
14
24
  console.log(" npm run serve");
15
25
  console.log("");
16
26
  console.log("Review the starter drafts, then commit the approved baseline.");
17
27
  }
18
28
 
29
+ function shellQuote(value) {
30
+ return `'${String(value).replaceAll("'", "'\\''")}'`;
31
+ }
32
+
19
33
  function parseArgs(argv) {
20
34
  let target;
21
35
  const options = {};
@@ -34,7 +48,9 @@ function parseArgs(argv) {
34
48
  else if (name === "no-install") options.install = false;
35
49
  else if (name === "company-name") options.companyName = next();
36
50
  else if (name === "policy-owner-name") options.policyOwnerName = next();
51
+ else if (name === "policy-owner-email") options.policyOwnerEmail = next();
37
52
  else if (name === "security-contact-email") options.securityContactEmail = next();
53
+ else if (name === "timezone") options.timezone = next();
38
54
  else if (name === "filegrc-version") options.filegrcVersion = next();
39
55
  else throw new Error(`Unknown option "${value}".`);
40
56
  }
@@ -44,18 +60,20 @@ function parseArgs(argv) {
44
60
  function printHelp() {
45
61
  console.log(`create-filegrc [directory] [options]
46
62
 
47
- Create a FileGRC workspace for a SOC 2 program.
63
+ Create a filegrc workspace for a SOC 2 program.
48
64
 
49
65
  Options:
50
- --company-name <name>
51
- --policy-owner-name <name>
52
- --security-contact-email <email>
66
+ --company-name <legal-name> Legal organization name
67
+ --policy-owner-name <name> Initial policy owner
68
+ --policy-owner-email <email> Policy owner's email address
69
+ --security-contact-email <email> Security reporting address
70
+ --timezone <iana-timezone> Program timezone, such as America/Chicago
53
71
  --filegrc-version <version> Override resolved engine version
54
- --no-install Write files and a preliminary lockfile only
55
- --force Allow a non-empty target without overwriting files
56
- --yes Use generic prompt defaults
57
- --version Show the package version
58
- --help Show this help`);
72
+ --no-install Write files and a preliminary lockfile only
73
+ --force Allow a non-empty target without overwriting files
74
+ --yes Use generic prompt defaults
75
+ --version Show the package version
76
+ --help Show this help`);
59
77
  }
60
78
 
61
79
  async function printVersion() {
package/src/defaults.js CHANGED
@@ -4,7 +4,6 @@ import { mkdir, writeFile } from "node:fs/promises";
4
4
  const FRAMEWORK_ID = "framework-aicpa-trust-services-criteria";
5
5
  const DESCRIPTION_FRAMEWORK_ID = "framework-aicpa-soc2-description-criteria";
6
6
  const OWNER_ID = "person-policy-owner";
7
- const INDEPENDENT_APPROVER_ID = "person-independent-approver";
8
7
  const OVERSIGHT_TEAM_ID = "team-security-risk-oversight";
9
8
  const INFORMATION_SECURITY_POLICY_ID = "policy-information-security";
10
9
  const DATA_POLICY_ID = "policy-data-protection-handling";
@@ -70,7 +69,7 @@ const controls = [
70
69
  title: "Security governance",
71
70
  statement: "Management assigns security responsibilities, and a reviewer who is separate from the policy owner and control operators independently reviews the program, risks, incidents, findings, policy approvals, and overdue work at least quarterly.",
72
71
  requirements: ["CC1.1", "CC1.2", "CC1.3", "CC1.5"],
73
- activity: "Assign an external independent reviewer and record quarterly oversight decisions, approvals, and actions.",
72
+ activity: "Assign an independent reviewer who is separate from program ownership and record quarterly oversight decisions, approvals, and actions.",
74
73
  controlType: "preventive",
75
74
  operationMode: "manual",
76
75
  frequency: "Quarterly",
@@ -963,21 +962,21 @@ export function baselineRecordFiles(effectiveDate) {
963
962
  id: OVERSIGHT_TEAM_ID,
964
963
  type: "team",
965
964
  title: "Security and Risk Oversight",
966
- status: "active",
965
+ status: "inactive",
967
966
  purpose: "Provide independent oversight of the security program, risk register, incidents, findings, vendor and access reviews, policy changes, exercises, and overdue work. The chair must be separate from the policy owner and people who operate the controls under review.",
968
- memberIds: [OWNER_ID, INDEPENDENT_APPROVER_ID],
969
- chairIds: [INDEPENDENT_APPROVER_ID],
967
+ memberIds: [OWNER_ID],
968
+ chairIds: [],
970
969
  meetingCadence: calendar("month", 3, effectiveDate)
971
970
  };
972
971
  const programRepository = {
973
972
  schemaVersion: 1,
974
973
  id: "system-filegrc-program-repository",
975
974
  type: "system",
976
- title: "FileGRC Program Repository",
975
+ title: "filegrc Program Repository",
977
976
  status: "active",
978
977
  criticality: "high",
979
978
  ownerIds: [OWNER_ID],
980
- description: "The Git repository that is authoritative for FileGRC governance records, approvals, exceptions, findings, acknowledgements, evidence indexes, and their revision history.",
979
+ description: "The Git repository that is authoritative for filegrc governance records, approvals, exceptions, findings, acknowledgements, evidence indexes, and their revision history.",
981
980
  systemKind: "governance-system-of-record",
982
981
  environment: "Git repository",
983
982
  dataClassification: "Confidential",
package/src/index.js CHANGED
@@ -10,18 +10,19 @@ const execute = promisify(execFile);
10
10
  const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
11
11
  const textExtensions = new Set(["", ".json", ".md", ".txt", ".yml", ".yaml", ".gitignore"]);
12
12
 
13
- export async function createFileGRC(options = {}) {
13
+ export async function createFilegrc(options = {}) {
14
14
  const parameterConfig = JSON.parse(await readFile(join(packageRoot, "template-parameters.json"), "utf8"));
15
15
  const target = resolve(options.target ?? "filegrc-program");
16
16
  await assertWritableTarget(target, Boolean(options.force));
17
17
  if (options.force) await assertNoTemplateCollisions(target);
18
18
 
19
19
  const prompted = await resolvePromptValues(parameterConfig.parameters, options);
20
- const engineVersion = await resolveFileGRCVersion(options.filegrcVersion);
20
+ const engineVersion = await resolveFilegrcVersion(options.filegrcVersion);
21
21
  const values = {
22
22
  ...prompted,
23
23
  effective_date: options.effectiveDate ?? new Date().toISOString().slice(0, 10),
24
24
  project_name: normalizePackageName(basename(target)),
25
+ filegrc_version: engineVersion,
25
26
  filegrc_version_range: `^${engineVersion}`
26
27
  };
27
28
 
@@ -29,17 +30,27 @@ export async function createFileGRC(options = {}) {
29
30
  await copyTemplate(target);
30
31
  await renderTemplate(target, parameterConfig, values);
31
32
  await writeBaselineRecords(target, values.effective_date);
33
+ const resourceCounts = await summarizeResources(target);
32
34
 
33
- if (options.install !== false) {
35
+ const installed = options.install !== false;
36
+ if (installed) {
34
37
  await run("npm", ["install", "--ignore-scripts"], target);
35
38
  } else {
36
39
  await writeMinimalLockfile(target, values.project_name, values.filegrc_version_range);
37
40
  }
38
- if (!await isInsideGitWorktree(target)) await run("git", ["init"], target);
39
- return { target, values, engineVersion };
41
+ const joinedExistingWorktree = await isInsideGitWorktree(target);
42
+ if (!joinedExistingWorktree) await run("git", ["init"], target);
43
+ return {
44
+ target,
45
+ values,
46
+ engineVersion,
47
+ resourceCounts,
48
+ install: installed ? "installed" : "skipped",
49
+ gitMode: joinedExistingWorktree ? "existing-worktree" : "initialized"
50
+ };
40
51
  }
41
52
 
42
- export async function resolveFileGRCVersion(explicitVersion) {
53
+ export async function resolveFilegrcVersion(explicitVersion) {
43
54
  if (explicitVersion) return cleanVersion(explicitVersion);
44
55
  try {
45
56
  const { stdout } = await execute("npm", ["view", "filegrc", "version", "--json"], {
@@ -58,7 +69,9 @@ async function resolvePromptValues(parameters, options) {
58
69
  const mapped = {
59
70
  company_name: options.companyName,
60
71
  policy_owner_name: options.policyOwnerName,
61
- security_contact_email: options.securityContactEmail
72
+ policy_owner_email: options.policyOwnerEmail,
73
+ security_contact_email: options.securityContactEmail,
74
+ timezone: options.timezone
62
75
  };
63
76
  if (options.yes) {
64
77
  mapped.company_name ??= "Example Company";
@@ -68,21 +81,29 @@ async function resolvePromptValues(parameters, options) {
68
81
  for (const key of Object.keys(mapped)) {
69
82
  if (mapped[key] !== undefined && mapped[key] !== null) mapped[key] = String(mapped[key]).trim();
70
83
  }
71
- const missing = parameters.filter(({ key, required }) => required && !mapped[key]);
72
- if (missing.length) {
73
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
74
- throw new Error(`Missing required values: ${missing.map(({ key }) => key).join(", ")}`);
75
- }
84
+ if (process.stdin.isTTY && process.stdout.isTTY) {
76
85
  const prompt = createInterface({ input: process.stdin, output: process.stdout });
77
86
  try {
78
- for (const parameter of missing) {
87
+ for (const parameter of parameters.filter(({ required, key }) => required && !mapped[key])) {
88
+ const defaultValue = parameterDefault(parameter, mapped);
89
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
79
90
  let value = "";
80
- while (!value) value = (await prompt.question(`${parameter.prompt}: `)).trim();
91
+ while (!value) {
92
+ value = (await prompt.question(`${parameter.prompt}${suffix}: `)).trim() || defaultValue;
93
+ }
81
94
  mapped[parameter.key] = value;
82
95
  }
83
96
  } finally {
84
97
  prompt.close();
85
98
  }
99
+ } else {
100
+ for (const parameter of parameters.filter(({ required, key }) => required && !mapped[key])) {
101
+ mapped[parameter.key] = parameterDefault(parameter, mapped);
102
+ }
103
+ }
104
+ const missing = parameters.filter(({ key, required }) => required && !mapped[key]);
105
+ if (missing.length) {
106
+ throw new Error(`Missing required values: ${missing.map(({ key }) => key).join(", ")}`);
86
107
  }
87
108
  for (const key of ["company_name", "policy_owner_name"]) {
88
109
  if (/[\u0000-\u001f\u007f]/.test(mapped[key])) {
@@ -93,12 +114,39 @@ async function resolvePromptValues(parameters, options) {
93
114
  throw new Error(`${key} cannot contain template token syntax.`);
94
115
  }
95
116
  }
96
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(mapped.security_contact_email)) {
97
- throw new Error("Security contact email must be a valid email address.");
117
+ for (const [key, label] of [
118
+ ["policy_owner_email", "Policy owner email"],
119
+ ["security_contact_email", "Security contact email"]
120
+ ]) {
121
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(mapped[key])) {
122
+ throw new Error(`${label} must be a valid email address.`);
123
+ }
98
124
  }
125
+ if (!isTimezone(mapped.timezone)) throw new Error("Program timezone must be a valid IANA time zone.");
99
126
  return mapped;
100
127
  }
101
128
 
129
+ function parameterDefault(parameter, mapped) {
130
+ if (parameter.defaultFrom) return mapped[parameter.defaultFrom] || "";
131
+ if (parameter.defaultSource === "local-timezone") return localTimezone();
132
+ return "";
133
+ }
134
+
135
+ function localTimezone() {
136
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
137
+ return isTimezone(timezone) ? timezone : "UTC";
138
+ }
139
+
140
+ function isTimezone(value) {
141
+ if (!value) return false;
142
+ try {
143
+ new Intl.DateTimeFormat("en-US", { timeZone: value }).format();
144
+ return true;
145
+ } catch {
146
+ return false;
147
+ }
148
+ }
149
+
102
150
  async function assertWritableTarget(target, force) {
103
151
  try {
104
152
  if ((await lstat(target)).isSymbolicLink()) {
@@ -167,9 +215,11 @@ async function renderTemplate(target, parameterConfig, values) {
167
215
 
168
216
  async function templateDestinationPaths() {
169
217
  const template = join(packageRoot, "template");
170
- return (await collectFiles(template)).map((source) => {
218
+ return (await collectFiles(template)).flatMap((source) => {
171
219
  const templatePath = relative(template, source);
172
- return templatePath === "gitignore" ? ".gitignore" : templatePath;
220
+ if (templatePath === "README.md") return [];
221
+ if (templatePath === "WORKSPACE.md") return ["README.md"];
222
+ return [templatePath === "gitignore" ? ".gitignore" : templatePath];
173
223
  });
174
224
  }
175
225
 
@@ -177,7 +227,10 @@ async function copyTemplate(target) {
177
227
  const template = join(packageRoot, "template");
178
228
  for (const source of await collectFiles(template)) {
179
229
  const templatePath = relative(template, source);
180
- const destinationPath = templatePath === "gitignore" ? ".gitignore" : templatePath;
230
+ if (templatePath === "README.md") continue;
231
+ const destinationPath = templatePath === "WORKSPACE.md"
232
+ ? "README.md"
233
+ : templatePath === "gitignore" ? ".gitignore" : templatePath;
181
234
  const destination = join(target, destinationPath);
182
235
  await assertNoSymlinkComponents(target, destinationPath);
183
236
  await mkdir(dirname(destination), { recursive: true });
@@ -199,16 +252,29 @@ async function collectFiles(directory) {
199
252
  return result;
200
253
  }
201
254
 
255
+ async function summarizeResources(target) {
256
+ const counts = {};
257
+ let total = 0;
258
+ for (const path of await collectFiles(join(target, "data"))) {
259
+ if (extname(path) !== ".json") continue;
260
+ const record = JSON.parse(await readFile(path, "utf8"));
261
+ if (!record?.id || !record?.type || !record?.schemaVersion) continue;
262
+ total += 1;
263
+ counts[record.type] = (counts[record.type] || 0) + 1;
264
+ }
265
+ return { total, byType: counts };
266
+ }
267
+
202
268
  async function writeMinimalLockfile(target, name, versionRange) {
203
269
  const lock = {
204
270
  name,
205
- version: "0.1.0",
271
+ version: "0.3.0",
206
272
  lockfileVersion: 3,
207
273
  requires: true,
208
274
  packages: {
209
275
  "": {
210
276
  name,
211
- version: "0.1.0",
277
+ version: "0.3.0",
212
278
  dependencies: { filegrc: versionRange }
213
279
  }
214
280
  }
@@ -237,7 +303,7 @@ async function run(command, args, cwd) {
237
303
  function cleanVersion(value) {
238
304
  const version = String(value ?? "").trim().replace(/^v/, "");
239
305
  if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
240
- throw new Error(`Could not resolve a valid FileGRC version from "${value}".`);
306
+ throw new Error(`Could not resolve a valid filegrc version from "${value}".`);
241
307
  }
242
308
  return version;
243
309
  }
@@ -1,8 +1,8 @@
1
- # FileGRC SOC 2 Workspace Instructions
1
+ # filegrc SOC 2 Workspace Instructions
2
2
 
3
3
  ## Purpose
4
4
 
5
- This repository is {{company_name}}’s FileGRC workspace for its SOC 2 program. Engineers and agents maintain the source records under `data/`. The `filegrc` package validates, searches, edits, and renders those files.
5
+ This repository is {{company_name}}’s filegrc workspace for its SOC 2 program. Engineers and agents maintain the source records under `data/`. The `filegrc` package validates, searches, edits, and renders those files.
6
6
 
7
7
  Using this repository does not establish compliance by itself. Records must match actual practice and evidence must prove that controls operated during the audit period.
8
8
 
@@ -12,11 +12,13 @@ Do not guess a resource type, field name, enum value, relationship, or file path
12
12
 
13
13
  ```sh
14
14
  npx filegrc guide --json
15
+ npx filegrc program-path --json
15
16
  npx filegrc guide risk-assessment --json
16
17
  npx filegrc list person --json
18
+ npx filegrc program-readiness --summary --json
17
19
  ```
18
20
 
19
- The first command lists every supported action and record type. The type guide gives the purpose, policy basis, timing, required and conditional fields, current relationship candidates, JSON location, and Markdown slots.
21
+ `program-path` gives agents the same six-step order, exact page Instructions, Use, Policy Basis, commands, current state, and next actions shown in the renderer. The general guide lists every supported action and record type. A type guide repeats that page guidance and adds timing, required and conditional fields, current relationship candidates, JSON location, and Markdown slots.
20
22
 
21
23
  For a new record, generate a mutation envelope:
22
24
 
@@ -44,7 +46,7 @@ Read `data/AGENTS.md` before changing records. More specific instructions inside
44
46
  - Let the local app generate IDs from each record’s name or title. When editing JSON directly, keep IDs globally unique, human-readable, and lowercase kebab-case.
45
47
  - Use ISO 8601 dates and RFC 3339 timestamps.
46
48
  - Store relationships as resource IDs.
47
- - Put policies, plans, charters, procedures, meeting minutes, training, assertions, narratives, templates, and audit responses in Markdown beside their JSON records. FileGRC derives the Markdown name, so records do not contain file paths.
49
+ - Put policies, plans, charters, procedures, meeting minutes, training, assertions, narratives, templates, and audit responses in Markdown beside their JSON records. filegrc derives the Markdown name, so records do not contain file paths.
48
50
  - Put signed forms, screenshots, third-party reports, and immutable exports behind evidence records. These files may be PDF, image, CSV, or another fixed format.
49
51
  - Never fetch an external evidence reference automatically.
50
52
  - Do not store secrets, credentials, session data, or personal data that may need to be erased from Git history.
@@ -93,7 +95,7 @@ Headless agents get the same protection by exporting an edit payload with `fileg
93
95
 
94
96
  `data/renderer.json` stores committed renderer preferences. New workspaces set `showOnboarding` to `true`. Completing or skipping onboarding sets it to `false`; the app does not commit that change.
95
97
 
96
- Onboarding explains the file and Git workflow, recurring obligations, event checklists, and bulk evidence preparation, then collects the initial service boundary, owner, business criticality, highest data classification, internet exposure, and optional audit objective. It creates or updates a `system` record and may create a planned `audit` record. Treat both as drafts to review against actual scope.
98
+ Onboarding explains the file and Git workflow, the program path, policy obligations, and Policy Events before covering report types and the final audit stage. It then collects the initial service boundary, owner, business criticality, highest data classification, internet exposure, and optional program goal. It creates or updates a `system` record and stores the management goal and program scope on `workspace`. Selecting Type 1 or Type 2 does not create an audit engagement. Completing onboarding opens the Step 1 overview so the user can add the real reviewers and operators, finish the oversight team, and confirm the criteria, commitments, vendors, and systems before approving policies.
97
99
 
98
100
  The renderer is optional. Agents may set `showOnboarding` to `false` and maintain all records headlessly. Restart onboarding from Repository when useful. Read-only builds never run it.
99
101
 
@@ -105,15 +107,15 @@ The generated workspace starts with the SOC 2 Security category:
105
107
  - The 33 Common Criteria reference IDs from CC1.1 through CC9.2, without the licensed criteria text
106
108
  - The nine Description Criteria reference IDs from DC1 through DC9, without the licensed criteria text
107
109
  - Planned controls mapped to those references and the included policies
108
- - A security and risk oversight team chaired by an external independent reviewer
110
+ - A security and risk oversight team chaired by an independent reviewer who may be internal or external
109
111
  - Recurring obligations for the reviews, scans, tests, training, and meetings required by the included policies
110
112
  - A default 5x5 risk method and Public, Internal, Confidential, and Restricted data classifications
111
113
 
112
- Treat every planned control as a proposal until its owner, scope, operation, and evidence match actual practice. Do not mark a control implemented because a policy describes it. Add Availability, Processing Integrity, Confidentiality, or Privacy criteria only when they are in scope.
114
+ Treat every planned control as a proposal until its owner, actual procedure in Record Markdown, system scope, cadence, authoritative evidence sources, implementation date, and mappings match actual practice. For a control linked to filegrc obligations, every non-retired Work Queue schedule must be enabled and its governing policies effective. Marking the control implemented starts eligible schedules. Do not mark a control implemented because a policy describes it. Add Availability, Processing Integrity, Confidentiality, or Privacy criteria only when they are in scope.
113
115
 
114
- The recurring obligations mirror the fixed cadences in the starter policies. Update the policy, control, and obligation together when an approved cadence changes. Create separate completion records, such as meetings, reviews, scans, tests, exercises, and attestations, for each period.
116
+ The recurring obligations mirror the fixed cadences in the starter policies. They remain proposals until every governing policy is active and effective and, when they name controls, at least one linked control is implemented. Update the policy, control, and obligation together when an approved cadence changes. Create separate completion records, such as meetings, reviews, scans, tests, exercises, and attestations, for each period.
115
117
 
116
- ## Policy work queue
118
+ ## Work Queue and Policy Events
117
119
 
118
120
  Run the same obligation planner used by the web app:
119
121
 
@@ -122,7 +124,9 @@ npx filegrc obligations --json
122
124
  npx filegrc obligations --from 2026-01-01 --through 2026-12-31 --complete --json
123
125
  ```
124
126
 
125
- A calendar obligation’s recurrence anchor starts its first allowed cycle. Unless `window` narrows that range, completion is allowed from the cycle start through the day before the next cycle, and the item becomes overdue on the next cycle’s first day. Use **Record work** in the obligation board, or create and link a completion atomically with:
127
+ Work Queue includes recurring obligations, Policy Event tasks, and every other open Action Item. Create an Action Item only when follow-up from a Finding, Risk, Incident, review, test, meeting, Exception, or request needs its own assignee, deadline, and completion proof. Point `sourceResourceId` to the record that produced the task. Use that source record’s Markdown for the report and observations.
128
+
129
+ A calendar obligation’s recurrence anchor starts its first allowed cycle. Unless `window` narrows that range, completion is allowed from the cycle start through the day before the next cycle, and the item becomes overdue on the next cycle’s first day. Use **Record work** in Work Queue, or create and link a completion atomically with:
126
130
 
127
131
  ```sh
128
132
  npx filegrc complete obligation-id completion-record.json
@@ -130,14 +134,14 @@ npx filegrc complete obligation-id completion-record.json
130
134
 
131
135
  Keep prior completion links because the planner matches each dated record to its own period.
132
136
 
133
- Event obligations are templates. Do not mark a template complete or replace it for each occurrence. Start a workflow in the obligation board or run:
137
+ Event obligations are templates. Do not mark a template complete or replace it for each occurrence. Use Trigger Work on Step 5 or run:
134
138
 
135
139
  ```sh
136
140
  npx filegrc trigger person-started --occurred-on 2026-07-25 --subject person-new-worker --json
137
141
  npx filegrc trigger person-ended --occurred-at 2026-07-25T16:30:00-05:00 --subject person-departing-worker --json
138
142
  ```
139
143
 
140
- The command creates one `obligation-event` and its complete action checklist in a single validated write. Hour-based deadlines require an RFC 3339 event timestamp so an immediate or 24-hour cutoff is exact. Day-based deadlines use the event’s calendar date. Link the requested completion resources and evidence to each action item, then mark the actions done and the event complete. Every generated action has a cutoff. FileGRC applies a 30-day deadline when a custom event obligation omits one.
144
+ Run `npx filegrc obligations` first to preview every task, owner, deadline, and requested proof for each available Policy Event. The trigger command creates one `obligation-event` and adds its full set of Action Items to the Work Queue in a single validated write. Its success output names the event, task count, task IDs, and deadlines. Hour-based deadlines require an RFC 3339 event timestamp so an immediate or 24-hour cutoff is exact. Day-based deadlines use the event’s calendar date. Link the requested completion resources and evidence to each action item, then mark the actions done and the event complete. Every generated action has a cutoff. filegrc applies a 30-day deadline when a custom event obligation omits one.
141
145
 
142
146
  Complete an event action and link its new proof in one validated write:
143
147
 
@@ -146,7 +150,7 @@ npx filegrc complete-action action-item-id completion-record.json --completed-on
146
150
  npx filegrc complete-event obligation-event-id --completed-on 2026-07-25
147
151
  ```
148
152
 
149
- FileGRC rejects a completion resource whose type does not match the obligation. It will close the event only after every action has its requested proof.
153
+ filegrc rejects a completion resource whose type does not match the obligation. It will close the event only after every action has its requested proof.
150
154
 
151
155
  ## Headless Markdown
152
156
 
@@ -157,11 +161,36 @@ npx filegrc content risk-assessment risk-assessment-2026 --json
157
161
  npx filegrc content risk-assessment risk-assessment-2026 --write updated-assessment.md
158
162
  ```
159
163
 
160
- Run `filegrc guide <type>` to get slot names. Policies use `content`, meetings use `agenda` and `minutes`, and implicit long-form work uses `record`. FileGRC derives the path and rejects content that does not belong to the record.
164
+ Run `filegrc guide <type>` to get slot names. Policies use `content`, meetings use `agenda` and `minutes`, and implicit long-form work uses `record`. filegrc derives the path and rejects content that does not belong to the record.
165
+
166
+ ## Program readiness and the candidate period
167
+
168
+ Prepare the management program before creating an audit engagement:
169
+
170
+ ```sh
171
+ npx filegrc program-readiness
172
+ npx filegrc program-readiness --require-ready --summary --json
173
+ ```
174
+
175
+ The Evidence Ready gate requires:
176
+
177
+ 1. A management goal, selected systems, criteria, and controls.
178
+ 2. Active policies with completed text, separate management approval, real approval and effective dates, and linked controls.
179
+ 3. Implemented controls with an owner, actual procedure, scope, cadence, evidence source, mappings, implementation date, and every eligible linked Work Queue schedule running.
180
+ 4. Active authoritative systems with evidence source roles, access owners, and repeatable extraction instructions in Record Markdown.
181
+ 5. A verified `test-export` or `test-capture` evidence record for each selected control family that relies on evidence from outside filegrc.
182
+
183
+ Completing onboarding creates draft External Evidence records only for evidence that must come from other systems and does not already have a dedicated Step 5 record. filegrc-managed records, such as risk assessments, meetings, vendor reviews, attestations, vulnerability scans, penetration tests, backup tests, exercises, exceptions, and findings, do not need a separate collection test. Put any fixed external artifact in an External Evidence record and link it from the operating record. For each generated draft, choose its authoritative source System, attach or reference the real result, record its collector and classification, then have another person verify it. Run `npx filegrc evidence-test-drafts` to create any drafts needed after the control set changes.
184
+
185
+ When the gate passes, set `workspace.candidatePeriodStart` to the date reliable evidence collection begins. Do not backdate it. `candidatePeriodStart` and `candidatePeriodEnd` express management’s target. They do not establish the final report period.
186
+
187
+ Maintain risk assessments and the risk register while the program operates. Complete assessments on schedule and after material changes, and add or update controls when the conclusions require a different response. Audit preparation still checks for a current, independently reviewed assessment.
188
+
189
+ Record complementary customer or subservice controls after the internal control set is defined. `complementary-control.relatedControlIds` is the source of truth for those links. filegrc derives the reverse connections for Control pages and evidence packets.
161
190
 
162
191
  ## Audit preparation and evidence packets
163
192
 
164
- After setting a Type 1 as-of date or Type 2 period, initialize the engagement-specific management work:
193
+ After engaging a CPA firm, create one audit record and set the firm-agreed Type 1 date or Type 2 period. Then initialize the engagement-specific management work:
165
194
 
166
195
  ```sh
167
196
  npx filegrc prepare-audit audit-2026-type-2
@@ -169,13 +198,22 @@ npx filegrc audit-readiness audit-2026-type-2
169
198
  npx filegrc audit-readiness audit-2026-type-2 --require-ready --json
170
199
  ```
171
200
 
201
+ The audit record’s `typeOneAsOf`, `periodStart`, and `periodEnd` are the dates agreed with the CPA firm. Keep the workspace candidate dates even when the formal period differs.
202
+
172
203
  Preparation creates a separate system description, management assertion, and management representation document for the engagement from the local starter templates. Type 2 preparation also creates a period completeness statement and one `audit-population` record for each standard population. It is safe to run again and does not approve documents, mark controls implemented, or create evidence. Do not reuse one completed management document across engagements.
173
204
 
205
+ Review both evidence paths against the exact firm-agreed date or period:
206
+
207
+ 1. filegrc Evidence consists of dated Step 5 operating records. Complete each applicable record, link it to the Controls it supports, record the result in structured fields or Markdown, and link any external artifact needed to support that result.
208
+ 2. External Evidence consists of verified `evidence` records from authoritative Systems. Confirm the source System, audit date or period, Control links, collector, verifier, and fixed attachment or approved external reference.
209
+
210
+ Audit Readiness reports coverage for both paths. The packet includes the matching filegrc records and Markdown with Git history, plus External Evidence records, retained attachments, delivery indexes, and checksums.
211
+
174
212
  Near the end of fieldwork, link a verified fixed-format copy of the signed management representation letter to its engagement-specific document. Date it on or after the Type 1 date or Type 2 period end. A representation that is still marked for later blocks packet delivery.
175
213
 
176
- Catalog each authoritative source under Systems and assign its `evidenceSourceKinds`. Name the people who can access its reports and keep extraction instructions in the system's Record Markdown. For each Type 2 population, select one source system and export the exact audit period. Split a population when different systems or queries produce its items. Link a verified `population-export` evidence record that names the same source system and stores the query or report parameters, generation time, timezone, count, completeness check, and accuracy check. A zero count still requires the source export and query. A population linked to an in-scope control cannot be marked not applicable.
214
+ Catalog each authoritative source under Systems and assign its `evidenceSourceKinds`. A third-party application is still a System because it operates controls or produces evidence. Create a separate Vendor for its provider and connect the System through `vendorId`; keep contracts, due diligence, and supplier risk on the Vendor. Name the people who can access system reports and keep extraction instructions in the System's Record Markdown. For each Type 2 population, select one source system and export the exact audit period. Split a population when different systems or queries produce its items. Link a verified `population-export` evidence record that names the same source system and stores the query or report parameters, generation time, timezone, count, completeness check, and accuracy check. A zero count still requires the source export and query. A population linked to an in-scope control cannot be marked not applicable.
177
215
 
178
- Every evidence record names its collector. Verified evidence also names its verifier and verification date. Use `sourceSystemId` for system exports, `sourceResourceIds` for FileGRC records, and `sourceCommit` to bind the evidence to repository state.
216
+ Every evidence record names its collector. Verified evidence also names its verifier and verification date. Use `sourceSystemId` for system exports, `sourceResourceIds` for filegrc records, and `sourceCommit` to bind the evidence to repository state.
179
217
 
180
218
  Preview coverage before writing output:
181
219
 
@@ -185,21 +223,23 @@ npx filegrc evidence-packet --audit audit-2026-type-2
185
223
  npx filegrc evidence-packet --audit audit-2026-type-2 --preview --require-ready
186
224
  ```
187
225
 
188
- The packet includes records explicitly related to the selected engagement, its systems, controls, criteria, policies, evidence, and dependencies. It does not include unrelated dated records from the workspace. A Type 2 packet adds period operating records, recurring obligation occurrences, event workflows, and management population reconciliations. Output includes a control matrix, source-system index, external-evidence delivery index, population index, evidence index, committed historical source versions, and SHA-256 checksums. Output under `.filegrc/evidence-packets/` is derived and must not be hand-edited or committed.
226
+ The packet includes records explicitly related to the selected engagement, its systems, controls, criteria, policies, evidence, and dependencies. It does not include unrelated dated records from the workspace. A Type 2 packet adds filegrc Evidence, recurring obligation occurrences, event workflows, and management population reconciliations. Output includes a control matrix with separate filegrc Evidence and External Evidence columns, source-system index, external-evidence delivery index, population index, evidence index, committed historical source versions, and SHA-256 checksums. Output under `.filegrc/evidence-packets/` is derived and must not be hand-edited or committed.
189
227
 
190
- Treat a packet as ready for management delivery only when its status is `delivery-ready`, its review list is clear, and its manifest names a clean Git revision. This means FileGRC's management checks passed. It does not mean the engagement team found the evidence sufficient or appropriate. The generator copies raw records, Markdown, and local fixed attachments. It never fetches external references. Reconcile `external-evidence-index.csv` to the auditor portal or other approved delivery system before telling the engagement team that submission is complete.
228
+ Treat a packet as ready for management delivery only when its status is `delivery-ready`, its review list is clear, and its manifest names a clean Git revision. This means filegrc's management checks passed. It does not mean the engagement team found the evidence sufficient or appropriate. The generator copies raw records, Markdown, and local fixed attachments. It never fetches external references. Reconcile `external-evidence-index.csv` to the auditor portal or other approved delivery system before telling the engagement team that submission is complete.
191
229
 
192
- Link a control test to its `audit-population` record when sampling applies. Link item-level sample evidence separately. Management owns population completeness and accuracy. The auditor owns sample selection, independent testing, exception evaluation, and the report opinion. The auditor or publisher also supplies the authoritative criteria and examination guidance. FileGRC stores references and orientation text, not licensed criteria.
230
+ Link a control test to its `audit-population` record when sampling applies. Link item-level sample evidence separately. Management owns population completeness and accuracy. The auditor owns sample selection, independent testing, exception evaluation, and the report opinion. The auditor or publisher also supplies the authoritative criteria and examination guidance. filegrc stores references and orientation text, not licensed criteria.
193
231
 
194
232
  ## Content and approvals
195
233
 
196
- The seed policy owner is {{policy_owner_name}} and the reporting address is {{security_contact_email}}. Replace ownership or contacts when responsibilities change.
234
+ The seed policy owner is {{policy_owner_name}} at {{policy_owner_email}}, and the security reporting address is {{security_contact_email}}. Replace ownership or contacts when responsibilities change.
235
+
236
+ Appoint an independent management reviewer during policy review, not as a condition of defining the service boundary. The reviewer must be separate from the policy owner and able to challenge the owner’s decisions. Most organizations assign another internal leader or manager. An external reviewer is also allowed, and a one-person company needs one because no second internal person is available. The reviewer chairs Security and Risk Oversight and approves policies and governed documents.
197
237
 
198
- The starter requires an external independent reviewer for SOC 2 oversight. This person must be separate from the policy owner and must not operate the controls they review. They chair Security and Risk Oversight and approve policies and governed documents. A one-person company must appoint a qualified person outside the company before approving the program. Do not use the audit firm for management or approval work without first confirming the firm's independence requirements.
238
+ The management reviewer and CPA auditor are different roles. Do not assign the CPA firm management or approval work without first confirming the firm's independence requirements.
199
239
 
200
240
  Policy and training attestations must identify the exact Git revision of the content that a person acknowledged. Store signatures as evidence attachments and link their evidence IDs from the attestation.
201
241
 
202
- Committee and risk meeting minutes are `meeting` resources with a primary Markdown companion. An optional `-agenda.md` companion holds the agenda. Record attendees, decisions, risks discussed, and action item IDs. Keep action tracking in `action-item` records.
242
+ Committee and risk meeting minutes are `meeting` resources with a primary Markdown companion. An optional `-agenda.md` companion holds the agenda. Record attendees, decisions, and risks discussed on the Meeting. When follow-up needs separate tracking, create an `action-item` whose `sourceResourceId` points to the Meeting.
203
243
 
204
244
  ## Audit evidence
205
245