create-filegrc 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -11,10 +11,37 @@ npm run serve
11
11
 
12
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
+ Creation has two layers. The five-record foundation contains workspace settings, the initial owner, the oversight team, renderer settings, and the filegrc system of record. The default `security` starter adds the framework references, proposed policies, controls, obligations, documents, and training records. Pass `--starter foundation` when you want to stop before selecting a framework.
15
+
16
+ For one noninteractive run, pass company and service fields together or use `--config setup.json`. The optional `setup` object defines the service boundary and management goal after installation. Use `--filegrc-package <directory>` to exercise unpublished local engine changes instead of installing the registry release. This writes a machine-local `file:` dependency, so replace it with a released version before sharing the generated workspace.
17
+
18
+ ```json
19
+ {
20
+ "companyName": "Example Company",
21
+ "policyOwnerName": "Security Owner",
22
+ "policyOwnerEmail": "owner@example.com",
23
+ "securityContactEmail": "security@example.com",
24
+ "timezone": "America/Chicago",
25
+ "starter": "security",
26
+ "setup": {
27
+ "serviceName": "Example Service",
28
+ "boundary": "The production service and supporting infrastructure.",
29
+ "criticality": "high",
30
+ "dataClassification": "Confidential",
31
+ "internetExposed": true,
32
+ "programGoal": "type-2"
33
+ }
34
+ }
35
+ ```
36
+
37
+ ```sh
38
+ npx create-filegrc@latest company-grc --config setup.json
39
+ ```
40
+
14
41
  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
42
 
16
43
  Git is initialized when needed. The browser can create local commits before a remote is configured.
17
44
 
18
45
  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
46
 
20
- Use `npx create-filegrc@latest --help` for non-interactive options.
47
+ Use `npx create-filegrc@latest --help` for noninteractive options.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-filegrc",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Create a filegrc workspace for a SOC 2 program",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/cli.js CHANGED
@@ -1,17 +1,45 @@
1
1
  import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
2
3
  import { createFilegrc } from "./index.js";
3
4
 
4
5
  export async function runCli(argv = process.argv.slice(2)) {
5
- const { target, options } = parseArgs(argv);
6
+ const parsed = parseArgs(argv);
7
+ const config = parsed.options.config ? await readConfig(parsed.options.config) : {};
8
+ const organization = config.organization && typeof config.organization === "object"
9
+ ? config.organization
10
+ : {};
11
+ const options = {
12
+ ...organization,
13
+ ...config,
14
+ ...parsed.options,
15
+ setup: {
16
+ ...(config.setup || {}),
17
+ ...parsed.setup
18
+ }
19
+ };
20
+ delete options.organization;
21
+ delete options.config;
22
+ delete options.target;
23
+ if (!Object.keys(options.setup).length) delete options.setup;
24
+ const target = parsed.target ?? config.target ?? "filegrc-program";
6
25
  if (options.help) return printHelp();
7
26
  if (options.version) return printVersion();
8
27
  const result = await createFilegrc({ target, ...options });
9
28
  console.log(`Created ${result.target}`);
10
- console.log(`filegrc ${result.engineVersion}: ${result.install === "installed" ? "installed" : "installation skipped"}`);
29
+ console.log(
30
+ `filegrc ${result.engineVersion}${result.enginePackage ? ` from ${result.enginePackage}` : ""}: ` +
31
+ `${result.install === "installed" ? "installed" : "installation skipped"}`
32
+ );
11
33
  console.log(`Git: ${result.gitMode === "existing-worktree" ? "joined existing worktree" : "initialized new repository"}`);
34
+ if (result.gitDetached) {
35
+ console.log("Warning: detached HEAD detected. Check out a branch before using browser commit, pull, or push.");
36
+ }
12
37
  console.log(`Timezone: ${result.values.timezone}`);
38
+ for (const stage of result.stages) {
39
+ console.log(`Stage ${stage.id}: ${stage.status}${stage.status === "created" ? ` (${stage.records} records)` : ""}`);
40
+ }
13
41
  console.log(
14
- `Program baseline: ${result.resourceCounts.total} records, including ` +
42
+ `${result.setup ? "Workspace after setup" : "Program baseline"}: ${result.resourceCounts.total} records, including ` +
15
43
  `${result.resourceCounts.byType.requirement || 0} requirements, ` +
16
44
  `${result.resourceCounts.byType.control || 0} controls, and ` +
17
45
  `${result.resourceCounts.byType.obligation || 0} obligations.`
@@ -19,20 +47,40 @@ export async function runCli(argv = process.argv.slice(2)) {
19
47
  console.log("");
20
48
  console.log(` cd ${shellQuote(result.target)}`);
21
49
  if (options.install === false) console.log(" npm install");
22
- console.log(" npx filegrc setup");
50
+ if (!result.setup) console.log(" npx filegrc setup");
51
+ if (result.setup) console.log(" npx filegrc program-path --next --json");
23
52
  console.log(" npm run validate");
24
53
  console.log(" npm run serve");
25
54
  console.log("");
26
- console.log("Review the starter drafts, then commit the approved baseline.");
55
+ if (result.setup) {
56
+ console.log(`Service setup: ${result.setup.system.id} (${result.setup.system.status}), target ${result.setup.target.assuranceGoal}.`);
57
+ if (result.setup.draft) {
58
+ console.log("Planned and in scope means selected for scope review, not approved or active.");
59
+ }
60
+ }
61
+ console.log("Immediate human decisions:");
62
+ console.log(result.setup?.target.assuranceGoal && result.setup.target.assuranceGoal !== "none"
63
+ ? ` 1. Confirm the selected assurance goal with management: ${assuranceGoalLabel(result.setup.target.assuranceGoal)}.`
64
+ : " 1. Select and confirm the assurance goal.");
65
+ console.log(" 2. Appoint an independent reviewer who is separate from the policy owner.");
66
+ console.log("Review the generated records, then commit the approved baseline.");
27
67
  }
28
68
 
29
69
  function shellQuote(value) {
30
70
  return `'${String(value).replaceAll("'", "'\\''")}'`;
31
71
  }
32
72
 
73
+ function assuranceGoalLabel(value) {
74
+ if (value === "soc-2-type-1") return "SOC 2 Type 1";
75
+ if (value === "soc-2-type-2") return "SOC 2 Type 2";
76
+ if (value === "readiness") return "Program Readiness";
77
+ return "No assurance goal selected";
78
+ }
79
+
33
80
  function parseArgs(argv) {
34
81
  let target;
35
82
  const options = {};
83
+ const setup = {};
36
84
  for (let index = 0; index < argv.length; index += 1) {
37
85
  const value = argv[index];
38
86
  if (!value.startsWith("-") && !target) {
@@ -46,15 +94,26 @@ function parseArgs(argv) {
46
94
  else if (name === "yes" || name === "y") options.yes = true;
47
95
  else if (name === "force") options.force = true;
48
96
  else if (name === "no-install") options.install = false;
97
+ else if (name === "config") options.config = next();
49
98
  else if (name === "company-name") options.companyName = next();
50
99
  else if (name === "policy-owner-name") options.policyOwnerName = next();
51
100
  else if (name === "policy-owner-email") options.policyOwnerEmail = next();
52
101
  else if (name === "security-contact-email") options.securityContactEmail = next();
53
102
  else if (name === "timezone") options.timezone = next();
103
+ else if (name === "starter") options.starter = next();
54
104
  else if (name === "filegrc-version") options.filegrcVersion = next();
105
+ else if (name === "filegrc-package") options.filegrcPackage = next();
106
+ else if (name === "service-name") setup.serviceName = next();
107
+ else if (name === "boundary") setup.boundary = next();
108
+ else if (name === "service-owner") setup.ownerId = next();
109
+ else if (name === "criticality") setup.criticality = next();
110
+ else if (name === "classification") setup.dataClassification = next();
111
+ else if (name === "internet-exposed") setup.internetExposed = booleanOption(next(), "internet-exposed");
112
+ else if (name === "program-goal") setup.programGoal = next();
113
+ else if (name === "setup-draft") setup.draft = true;
55
114
  else throw new Error(`Unknown option "${value}".`);
56
115
  }
57
- return { target: target ?? "filegrc-program", options };
116
+ return { target, options, setup };
58
117
  }
59
118
 
60
119
  function printHelp() {
@@ -63,12 +122,23 @@ function printHelp() {
63
122
  Create a filegrc workspace for a SOC 2 program.
64
123
 
65
124
  Options:
125
+ --config <json-file|-> Read creation and optional setup values
66
126
  --company-name <legal-name> Legal organization name
67
127
  --policy-owner-name <name> Initial policy owner
68
128
  --policy-owner-email <email> Policy owner's email address
69
129
  --security-contact-email <email> Security reporting address
70
130
  --timezone <iana-timezone> Program timezone, such as America/Chicago
131
+ --starter <profile> security (default) or foundation
71
132
  --filegrc-version <version> Override resolved engine version
133
+ --filegrc-package <directory> Install an unpublished local filegrc package
134
+ --service-name <name> Complete service setup after creation
135
+ --boundary <description> Initial service boundary
136
+ --service-owner <person-id> Defaults to person-policy-owner
137
+ --criticality <level> low, medium, high, or critical
138
+ --classification <name> Initial data classification
139
+ --internet-exposed <bool> true or false
140
+ --program-goal <goal> none, readiness, type-1, or type-2
141
+ --setup-draft Save combined service setup as a draft
72
142
  --no-install Write files and a preliminary lockfile only
73
143
  --force Allow a non-empty target without overwriting files
74
144
  --yes Use generic prompt defaults
@@ -80,3 +150,26 @@ async function printVersion() {
80
150
  const packageJson = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
81
151
  console.log(packageJson.version);
82
152
  }
153
+
154
+ async function readConfig(path) {
155
+ const source = path === "-"
156
+ ? await readStdin()
157
+ : await readFile(resolve(String(path)), "utf8");
158
+ const parsed = JSON.parse(source);
159
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
160
+ throw new Error("Creation config must be a JSON object.");
161
+ }
162
+ return parsed;
163
+ }
164
+
165
+ async function readStdin() {
166
+ const chunks = [];
167
+ for await (const chunk of process.stdin) chunks.push(chunk);
168
+ return Buffer.concat(chunks).toString("utf8");
169
+ }
170
+
171
+ function booleanOption(value, name) {
172
+ if (value === "true") return true;
173
+ if (value === "false") return false;
174
+ throw new Error(`--${name} must be true or false.`);
175
+ }
package/src/defaults.js CHANGED
@@ -884,11 +884,11 @@ const obligations = [
884
884
  }
885
885
  ];
886
886
 
887
- export function baselineRecordPaths() {
888
- return baselineRecordFiles("2000-01-01").map(({ path }) => path);
887
+ export function baselineRecordPaths(starter = "security") {
888
+ return baselineRecordFiles("2000-01-01", starter).map(({ path }) => path);
889
889
  }
890
890
 
891
- export function baselineRecordFiles(effectiveDate) {
891
+ export function baselineRecordFiles(effectiveDate, starter = "security") {
892
892
  const framework = {
893
893
  schemaVersion: 1,
894
894
  id: FRAMEWORK_ID,
@@ -1006,20 +1006,25 @@ export function baselineRecordFiles(effectiveDate) {
1006
1006
  policyIds: obligation.policyIds
1007
1007
  }));
1008
1008
 
1009
+ const foundation = [
1010
+ recordFile("systems", programRepository),
1011
+ recordFile("teams", team)
1012
+ ];
1013
+ if (starter === "foundation") return foundation;
1014
+ if (starter !== "security") throw new Error(`Unknown starter profile "${starter}".`);
1009
1015
  return [
1010
1016
  recordFile("frameworks", framework),
1011
1017
  recordFile("frameworks", descriptionFramework),
1012
1018
  ...commonRequirements.map((record) => recordFile("requirements", record)),
1013
1019
  ...descriptionRequirements.map((record) => recordFile("requirements", record)),
1014
1020
  ...controlRecords.map((record) => recordFile("controls", record)),
1015
- recordFile("systems", programRepository),
1016
- recordFile("teams", team),
1021
+ ...foundation,
1017
1022
  ...obligationRecords.map((record) => recordFile("obligations", record))
1018
1023
  ];
1019
1024
  }
1020
1025
 
1021
- export async function writeBaselineRecords(target, effectiveDate) {
1022
- for (const { path: relativePath, record } of baselineRecordFiles(effectiveDate)) {
1026
+ export async function writeBaselineRecords(target, effectiveDate, starter = "security") {
1027
+ for (const { path: relativePath, record } of baselineRecordFiles(effectiveDate, starter)) {
1023
1028
  const path = join(target, relativePath);
1024
1029
  await mkdir(dirname(path), { recursive: true });
1025
1030
  await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
package/src/index.js CHANGED
@@ -4,33 +4,43 @@ import { basename, dirname, extname, join, relative, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { promisify } from "node:util";
6
6
  import { createInterface } from "node:readline/promises";
7
- import { baselineRecordPaths, writeBaselineRecords } from "./defaults.js";
7
+ import { baselineRecordFiles, baselineRecordPaths, writeBaselineRecords } from "./defaults.js";
8
8
 
9
9
  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
+ const STARTER_PROFILES = new Set(["foundation", "security"]);
13
+ const SECURITY_TEMPLATE_COLLECTIONS = new Set(["documents", "policies", "training"]);
12
14
 
13
15
  export async function createFilegrc(options = {}) {
14
16
  const parameterConfig = JSON.parse(await readFile(join(packageRoot, "template-parameters.json"), "utf8"));
15
17
  const target = resolve(options.target ?? "filegrc-program");
18
+ const starter = normalizeStarterProfile(options.starter);
19
+ if (options.setup && options.install === false) {
20
+ throw new Error("Combined service setup requires installation. Remove --no-install or run filegrc setup after npm install.");
21
+ }
22
+ validateCombinedSetup(options.setup);
16
23
  await assertWritableTarget(target, Boolean(options.force));
17
- if (options.force) await assertNoTemplateCollisions(target);
24
+ if (options.force) await assertNoTemplateCollisions(target, starter);
18
25
 
19
26
  const prompted = await resolvePromptValues(parameterConfig.parameters, options);
20
- const engineVersion = await resolveFilegrcVersion(options.filegrcVersion);
27
+ const engine = await resolveEngine(options);
28
+ const starterText = starterTemplateText(starter, prompted.company_name);
21
29
  const values = {
22
30
  ...prompted,
23
31
  effective_date: options.effectiveDate ?? new Date().toISOString().slice(0, 10),
24
32
  project_name: normalizePackageName(basename(target)),
25
- filegrc_version: engineVersion,
26
- filegrc_version_range: `^${engineVersion}`
33
+ filegrc_version: engine.version,
34
+ filegrc_version_range: engine.dependency,
35
+ ...starterText
27
36
  };
28
37
 
29
38
  await mkdir(target, { recursive: true });
30
- await copyTemplate(target);
31
- await renderTemplate(target, parameterConfig, values);
32
- await writeBaselineRecords(target, values.effective_date);
33
- const resourceCounts = await summarizeResources(target);
39
+ await copyTemplate(target, starter);
40
+ await renderTemplate(target, parameterConfig, values, starter);
41
+ await writeBaselineRecords(target, values.effective_date, starter);
42
+ await applyStarterScope(target, starter, values.effective_date);
43
+ const initialResourceCounts = await summarizeResources(target);
34
44
 
35
45
  const installed = options.install !== false;
36
46
  if (installed) {
@@ -40,16 +50,36 @@ export async function createFilegrc(options = {}) {
40
50
  }
41
51
  const joinedExistingWorktree = await isInsideGitWorktree(target);
42
52
  if (!joinedExistingWorktree) await run("git", ["init"], target);
53
+ const gitHead = await inspectGitHead(target);
54
+ const setup = options.setup ? await runCombinedSetup(target, options.setup) : null;
55
+ const resourceCounts = setup ? await summarizeResources(target) : initialResourceCounts;
43
56
  return {
44
57
  target,
45
58
  values,
46
- engineVersion,
59
+ starter,
60
+ engineVersion: engine.version,
61
+ engineSource: engine.localPath ? "local" : "registry",
62
+ enginePackage: engine.localPath || null,
63
+ dependency: engine.dependency,
64
+ stages: starterStages(starter, initialResourceCounts),
47
65
  resourceCounts,
66
+ setup,
48
67
  install: installed ? "installed" : "skipped",
49
- gitMode: joinedExistingWorktree ? "existing-worktree" : "initialized"
68
+ gitMode: joinedExistingWorktree ? "existing-worktree" : "initialized",
69
+ gitBranch: gitHead.branch,
70
+ gitDetached: gitHead.detached
50
71
  };
51
72
  }
52
73
 
74
+ export function normalizeStarterProfile(value = "security") {
75
+ const profile = String(value || "security").trim().toLowerCase();
76
+ const normalized = profile === "empty" ? "foundation" : profile === "soc2-security" ? "security" : profile;
77
+ if (!STARTER_PROFILES.has(normalized)) {
78
+ throw new Error(`Starter profile must be one of ${[...STARTER_PROFILES].join(", ")}.`);
79
+ }
80
+ return normalized;
81
+ }
82
+
53
83
  export async function resolveFilegrcVersion(explicitVersion) {
54
84
  if (explicitVersion) return cleanVersion(explicitVersion);
55
85
  try {
@@ -65,6 +95,28 @@ export async function resolveFilegrcVersion(explicitVersion) {
65
95
  }
66
96
  }
67
97
 
98
+ async function resolveEngine(options) {
99
+ if (options.filegrcVersion && options.filegrcPackage) {
100
+ throw new Error("Use either filegrcVersion or filegrcPackage, not both.");
101
+ }
102
+ if (!options.filegrcPackage) {
103
+ const version = await resolveFilegrcVersion(options.filegrcVersion);
104
+ return { version, dependency: `^${version}`, localPath: null };
105
+ }
106
+ const localPath = resolve(String(options.filegrcPackage));
107
+ let packageJson;
108
+ try {
109
+ packageJson = JSON.parse(await readFile(join(localPath, "package.json"), "utf8"));
110
+ } catch (error) {
111
+ throw new Error(`Could not read a local filegrc package at ${localPath}: ${error.message}`);
112
+ }
113
+ if (packageJson.name !== "filegrc") {
114
+ throw new Error(`Local package at ${localPath} is named "${packageJson.name || ""}", expected "filegrc".`);
115
+ }
116
+ const version = cleanVersion(packageJson.version);
117
+ return { version, dependency: `file:${localPath.replaceAll("\\", "/")}`, localPath };
118
+ }
119
+
68
120
  async function resolvePromptValues(parameters, options) {
69
121
  const mapped = {
70
122
  company_name: options.companyName,
@@ -161,9 +213,9 @@ async function assertWritableTarget(target, force) {
161
213
  }
162
214
  }
163
215
 
164
- async function assertNoTemplateCollisions(target) {
216
+ async function assertNoTemplateCollisions(target, starter) {
165
217
  const collisions = [];
166
- for (const destinationPath of [...await templateDestinationPaths(), ...baselineRecordPaths()]) {
218
+ for (const destinationPath of [...await templateDestinationPaths(starter), ...baselineRecordPaths(starter)]) {
167
219
  await assertNoSymlinkComponents(target, destinationPath);
168
220
  try {
169
221
  await lstat(join(target, destinationPath));
@@ -192,20 +244,22 @@ async function assertNoSymlinkComponents(target, relativePath) {
192
244
  }
193
245
  }
194
246
 
195
- async function renderTemplate(target, parameterConfig, values) {
247
+ async function renderTemplate(target, parameterConfig, values, starter) {
196
248
  const declared = new Set([
197
249
  ...parameterConfig.parameters.map(({ key }) => key),
198
250
  ...parameterConfig.generated.map(({ key }) => key)
199
251
  ]);
200
- const files = (await templateDestinationPaths()).map((path) => join(target, path));
252
+ const files = (await templateDestinationPaths(starter)).map((path) => join(target, path));
201
253
  for (const path of files) {
202
254
  if (!textExtensions.has(extname(path)) && basename(path) !== ".gitignore") continue;
203
255
  let source = await readFile(path, "utf8");
204
256
  const jsonFile = extname(path) === ".json";
205
- source = source.replace(/\{\{([a-z0-9_]+)\}\}/g, (match, token) => {
257
+ source = source.replace(/\{\{([a-z0-9_]+)\}\}(\.)?/g, (match, token, sentencePeriod = "") => {
206
258
  if (!declared.has(token)) throw new Error(`Unknown template token "{{${token}}}" in ${path}`);
207
259
  if (values[token] === undefined) throw new Error(`No value resolved for template token "{{${token}}}"`);
208
- return jsonFile ? jsonStringContents(values[token]) : String(values[token]);
260
+ const value = jsonFile ? jsonStringContents(values[token]) : String(values[token]);
261
+ const punctuation = !jsonFile && sentencePeriod && /[.!?]$/u.test(value) ? "" : sentencePeriod;
262
+ return value + punctuation;
209
263
  });
210
264
  const unresolved = /\{\{([a-z0-9_]+)\}\}/.exec(source);
211
265
  if (unresolved) throw new Error(`Unresolved template token "{{${unresolved[1]}}}" in ${path}`);
@@ -213,20 +267,22 @@ async function renderTemplate(target, parameterConfig, values) {
213
267
  }
214
268
  }
215
269
 
216
- async function templateDestinationPaths() {
270
+ async function templateDestinationPaths(starter = "security") {
217
271
  const template = join(packageRoot, "template");
218
272
  return (await collectFiles(template)).flatMap((source) => {
219
273
  const templatePath = relative(template, source);
274
+ if (!includeTemplatePath(templatePath, starter)) return [];
220
275
  if (templatePath === "README.md") return [];
221
276
  if (templatePath === "WORKSPACE.md") return ["README.md"];
222
277
  return [templatePath === "gitignore" ? ".gitignore" : templatePath];
223
278
  });
224
279
  }
225
280
 
226
- async function copyTemplate(target) {
281
+ async function copyTemplate(target, starter = "security") {
227
282
  const template = join(packageRoot, "template");
228
283
  for (const source of await collectFiles(template)) {
229
284
  const templatePath = relative(template, source);
285
+ if (!includeTemplatePath(templatePath, starter)) continue;
230
286
  if (templatePath === "README.md") continue;
231
287
  const destinationPath = templatePath === "WORKSPACE.md"
232
288
  ? "README.md"
@@ -242,6 +298,13 @@ async function copyTemplate(target) {
242
298
  }
243
299
  }
244
300
 
301
+ function includeTemplatePath(templatePath, starter) {
302
+ if (starter === "security") return true;
303
+ const segments = templatePath.split(/[\\/]/);
304
+ if (segments[0] !== "data" || !SECURITY_TEMPLATE_COLLECTIONS.has(segments[1])) return true;
305
+ return segments.at(-1) === "AGENTS.md";
306
+ }
307
+
245
308
  async function collectFiles(directory) {
246
309
  const result = [];
247
310
  for (const item of await readdir(directory, { withFileTypes: true })) {
@@ -265,16 +328,137 @@ async function summarizeResources(target) {
265
328
  return { total, byType: counts };
266
329
  }
267
330
 
331
+ async function applyStarterScope(target, starter, effectiveDate) {
332
+ if (starter !== "security") return;
333
+ const records = baselineRecordFiles(effectiveDate, starter).map(({ record }) => record);
334
+ const workspacePath = join(target, "data", "workspace.json");
335
+ const workspace = JSON.parse(await readFile(workspacePath, "utf8"));
336
+ const ids = (type) => records.filter((record) => record.type === type).map((record) => record.id);
337
+ await writeFile(workspacePath, `${JSON.stringify({
338
+ ...workspace,
339
+ frameworkIds: ids("framework"),
340
+ requirementIds: ids("requirement"),
341
+ controlIds: ids("control")
342
+ }, null, 2)}\n`, "utf8");
343
+ }
344
+
345
+ function starterStages(starter, counts) {
346
+ const foundationTypes = new Set(["workspace", "renderer-settings", "person", "team", "system"]);
347
+ const foundation = Object.entries(counts.byType)
348
+ .filter(([type]) => foundationTypes.has(type))
349
+ .reduce((total, [, count]) => total + count, 0);
350
+ return [
351
+ { id: "foundation", status: "created", records: foundation },
352
+ {
353
+ id: "soc2-security",
354
+ status: starter === "security" ? "created" : "skipped",
355
+ records: starter === "security" ? counts.total - foundation : 0
356
+ }
357
+ ];
358
+ }
359
+
360
+ function starterTemplateText(starter, companyName) {
361
+ if (starter === "foundation") {
362
+ return {
363
+ program_title: `${companyName} GRC Program`,
364
+ program_description: "Governance, risk, and compliance workspace without a preselected framework.",
365
+ program_summary: `This private workspace holds ${companyName}'s governance, risk, compliance, and audit-evidence records. JSON under \`data/\` stores structured records, Markdown stores long-form work, and Git records reviewed changes.`,
366
+ agent_title: "filegrc Workspace Instructions",
367
+ agent_purpose: `This repository is ${companyName}’s foundation filegrc workspace. Engineers and agents maintain the source records under \`data/\`. The \`filegrc\` package validates, searches, edits, and renders those files. No framework or assurance program has been selected yet.`,
368
+ starter_baseline: `## Foundation baseline
369
+
370
+ The generated workspace starts with five structural records:
371
+
372
+ - Workspace and renderer settings
373
+ - The initial active owner
374
+ - An inactive security and risk oversight team that still needs an independent chair
375
+ - The filegrc Git repository as a governance system of record
376
+ - A default 5x5 risk method and Public, Internal, Confidential, and Restricted data classifications
377
+
378
+ This profile does not include framework requirements, policies, governed documents, training, controls, obligations, or audit-management templates. Add and review those records for the selected framework before treating Program Readiness or Audit Readiness as meaningful. Do not infer that an absent control, policy, or schedule is unnecessary.`,
379
+ audit_preparation_guidance: "The foundation profile does not include the local SOC 2 management-document templates used by `prepare-audit`. Add reviewed templates and program scope before initializing audit work. Audit preparation must not invent missing policy, control, or evidence facts.",
380
+ starter_setup: `## Start the program
381
+
382
+ This foundation profile contains the workspace, initial owner, oversight team, renderer settings, and filegrc system of record. It does not select a framework or create proposed policies, controls, obligations, or evidence.
383
+
384
+ 1. Run \`npx filegrc setup\` for guided service and goal setup, or use browser onboarding.
385
+ 2. Use \`npx filegrc guide --json\` before creating framework requirements, policies, controls, obligations, and evidence sources.
386
+ 3. Run \`npx filegrc validate\`, review the Git diff, and commit each reviewed program layer.`
387
+ };
388
+ }
389
+ return {
390
+ program_title: `${companyName} SOC 2 Program`,
391
+ program_description: "SOC 2 Security program based on the AICPA Trust Services Criteria.",
392
+ program_summary: `This private workspace holds ${companyName}'s SOC 2 program records and audit evidence. JSON under \`data/\` stores structured records, Markdown stores long-form work, and Git records reviewed changes.`,
393
+ agent_title: "filegrc SOC 2 Workspace Instructions",
394
+ agent_purpose: `This repository is ${companyName}’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.`,
395
+ starter_baseline: `## Starter baseline
396
+
397
+ The generated workspace starts with the SOC 2 Security category:
398
+
399
+ - Active framework records for the 2017 Trust Services Criteria with revised points of focus (2022) and the 2018 SOC 2 Description Criteria with revised implementation guidance (2022)
400
+ - The 33 Common Criteria reference IDs from CC1.1 through CC9.2, without the licensed criteria text
401
+ - The nine Description Criteria reference IDs from DC1 through DC9, without the licensed criteria text
402
+ - Planned controls mapped to those references and the included policies
403
+ - A security and risk oversight team chaired by an independent reviewer who may be internal or external
404
+ - Recurring obligations for the reviews, scans, tests, training, and meetings required by the included policies
405
+ - A default 5x5 risk method and Public, Internal, Confidential, and Restricted data classifications
406
+
407
+ 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.
408
+
409
+ 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.`,
410
+ audit_preparation_guidance: "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.",
411
+ starter_setup: `## Finish initial setup
412
+
413
+ The starter policies, controls, and obligations are proposals. They do not state that ${companyName} operates the described controls.
414
+
415
+ 1. Run \`npx filegrc setup\` for guided service and goal setup, or use browser onboarding. Then finish Step 1 by adding the real reviewers and operators, finishing the oversight team, and confirming applicable criteria, commitments, material vendors, and in-scope systems.
416
+ 2. Review the starter policies, appoint a reviewer who is separate from the policy owner, and activate only the policies that match current practice. The reviewer will usually be another person in the organization, but may be external.
417
+ 3. Review the starter control set, implement each applicable control with its actual procedure, scope, cadence, evidence sources, and implementation date, and confirm any linked Work Queue schedules are enabled. Marking a control implemented starts eligible schedules. Then record any complementary customer or subservice controls.
418
+ 4. Preview External Evidence drafts with \`npx filegrc evidence-test-drafts --preview --json\`. Create them only after confirming applicable controls and authoritative source systems.
419
+ 5. Run \`npx filegrc program-readiness --require-ready\`, record the management candidate period start when reliable evidence collection begins, maintain risk assessments and risks, update controls when needed, use Work Queue for scheduled work, and trigger Policy Events when changes create required actions.
420
+ 6. Engage a CPA firm, record the separate firm-agreed period in an audit record, review filegrc Evidence and External Evidence, and prepare fieldwork.`
421
+ };
422
+ }
423
+
424
+ function validateCombinedSetup(setup) {
425
+ if (!setup) return;
426
+ if (Array.isArray(setup) || typeof setup !== "object") throw new Error("setup must be a JSON object.");
427
+ const required = ["serviceName", "boundary", "criticality", "dataClassification", "internetExposed"];
428
+ const missing = required.filter((name) => setup[name] === undefined || setup[name] === "");
429
+ if (missing.length) throw new Error(`Combined setup is missing: ${missing.join(", ")}.`);
430
+ }
431
+
432
+ async function runCombinedSetup(target, input) {
433
+ const setup = { programGoal: "none", ownerId: "person-policy-owner", ...input };
434
+ const args = [
435
+ join(target, "node_modules", "filegrc", "bin", "filegrc.js"),
436
+ "setup",
437
+ "--service-name", String(setup.serviceName),
438
+ "--boundary", String(setup.boundary),
439
+ "--owner", String(setup.ownerId),
440
+ "--criticality", String(setup.criticality),
441
+ "--classification", String(setup.dataClassification),
442
+ "--internet-exposed", String(setup.internetExposed),
443
+ "--program-goal", String(setup.programGoal),
444
+ "--summary",
445
+ "--json"
446
+ ];
447
+ if (setup.draft === true) args.push("--draft");
448
+ const result = await run(process.execPath, args, target);
449
+ return JSON.parse(result.stdout);
450
+ }
451
+
268
452
  async function writeMinimalLockfile(target, name, versionRange) {
269
453
  const lock = {
270
454
  name,
271
- version: "0.3.0",
455
+ version: "0.3.2",
272
456
  lockfileVersion: 3,
273
457
  requires: true,
274
458
  packages: {
275
459
  "": {
276
460
  name,
277
- version: "0.3.0",
461
+ version: "0.3.2",
278
462
  dependencies: { filegrc: versionRange }
279
463
  }
280
464
  }
@@ -291,6 +475,20 @@ async function isInsideGitWorktree(target) {
291
475
  }
292
476
  }
293
477
 
478
+ async function inspectGitHead(target) {
479
+ try {
480
+ const { stdout } = await execute("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], { cwd: target });
481
+ return { branch: stdout.trim() || null, detached: false };
482
+ } catch {
483
+ try {
484
+ await execute("git", ["rev-parse", "--verify", "HEAD"], { cwd: target });
485
+ return { branch: null, detached: true };
486
+ } catch {
487
+ return { branch: null, detached: false };
488
+ }
489
+ }
490
+ }
491
+
294
492
  async function run(command, args, cwd) {
295
493
  try {
296
494
  return await execute(command, args, { cwd, maxBuffer: 10_000_000 });
@@ -1,8 +1,8 @@
1
- # filegrc SOC 2 Workspace Instructions
1
+ # {{agent_title}}
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
+ {{agent_purpose}}
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,13 +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
+ npx filegrc program-path --next --json
16
16
  npx filegrc guide risk-assessment --json
17
17
  npx filegrc list person --json
18
18
  npx filegrc program-readiness --summary --json
19
19
  ```
20
20
 
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.
21
+ `program-path --next --json` gives agents the current step and first action. Use `--summary` for all six step statuses or `--current` for the current step’s full renderer Instructions, Use, Policy Basis, commands, and next actions. 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.
22
22
 
23
23
  For a new record, generate a mutation envelope:
24
24
 
@@ -95,25 +95,11 @@ Headless agents get the same protection by exporting an edit payload with `fileg
95
95
 
96
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.
97
97
 
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.
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 one `system` record and stores that selected system and the management goal on `workspace`. It does not select framework records, link controls to the service, or create evidence. 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.
99
99
 
100
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.
101
101
 
102
- ## Starter baseline
103
-
104
- The generated workspace starts with the SOC 2 Security category:
105
-
106
- - Active framework records for the 2017 Trust Services Criteria with revised points of focus (2022) and the 2018 SOC 2 Description Criteria with revised implementation guidance (2022)
107
- - The 33 Common Criteria reference IDs from CC1.1 through CC9.2, without the licensed criteria text
108
- - The nine Description Criteria reference IDs from DC1 through DC9, without the licensed criteria text
109
- - Planned controls mapped to those references and the included policies
110
- - A security and risk oversight team chaired by an independent reviewer who may be internal or external
111
- - Recurring obligations for the reviews, scans, tests, training, and meetings required by the included policies
112
- - A default 5x5 risk method and Public, Internal, Confidential, and Restricted data classifications
113
-
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.
115
-
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.
102
+ {{starter_baseline}}
117
103
 
118
104
  ## Work Queue and Policy Events
119
105
 
@@ -180,7 +166,7 @@ The Evidence Ready gate requires:
180
166
  4. Active authoritative systems with evidence source roles, access owners, and repeatable extraction instructions in Record Markdown.
181
167
  5. A verified `test-export` or `test-capture` evidence record for each selected control family that relies on evidence from outside filegrc.
182
168
 
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.
169
+ Onboarding does not create External Evidence records. After confirming the applicable controls and authoritative source Systems, run `npx filegrc evidence-test-drafts --preview --json` and review the proposed collection tests. Then run `npx filegrc evidence-test-drafts` to create the missing drafts. 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 created draft, choose its authoritative source System, attach or reference the real result, record its collector and classification, then have another person verify it.
184
170
 
185
171
  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
172
 
@@ -200,7 +186,7 @@ npx filegrc audit-readiness audit-2026-type-2 --require-ready --json
200
186
 
201
187
  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
188
 
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.
189
+ {{audit_preparation_guidance}}
204
190
 
205
191
  Review both evidence paths against the exact firm-agreed date or period:
206
192
 
@@ -4,28 +4,9 @@
4
4
 
5
5
  Run a SOC 2 program as files in Git.
6
6
 
7
- filegrc gives a founder-led engineering team one place to adopt policies, implement controls, test External Evidence collection, run recurring compliance work, and prepare an audit. JSON holds structured records, Markdown holds long-form work, and Git supplies the change history.
7
+ filegrc gives founder-led engineering teams one place to adopt policies, implement controls, run recurring work, collect evidence, and prepare an audit.
8
8
 
9
- There is no separate application database. The repository is the program, so engineers and agents can use the same data through the web app, a text editor, or the CLI.
10
-
11
- ## Why it exists
12
-
13
- SOC 2 work tends to scatter across documents, calendars, tickets, screenshots, and the auditor’s request list. That makes it hard to answer basic questions: What is due? Which policy requires it? What changed during the audit period? Is the evidence complete?
14
-
15
- filegrc keeps that work connected:
16
-
17
- - A starter Security program links criteria references, policies, planned controls, owners, and schedules.
18
- - Work Queue turns policy timing into upcoming, due, and overdue work.
19
- - Policy Events add the required hiring, departure, vendor, incident, and change tasks to the Work Queue.
20
- - Program Readiness says whether management can begin a candidate Type 2 evidence period without an audit record.
21
- - Audit Readiness starts later with the CPA engagement, formal period, fieldwork documents, populations, and evidence delivery.
22
- - The packet builder produces a scoped, indexed delivery with source files, attachments, history, and checksums.
23
-
24
- The starter content is a proposal, not a claim of compliance. Review every policy and planned control against how your company actually operates before approving it.
25
-
26
- ## Start a workspace
27
-
28
- You need Node.js 20 or newer and Git.
9
+ It is open source, MIT licensed, and runs locally.
29
10
 
30
11
  ```sh
31
12
  npx create-filegrc@latest company-grc
@@ -34,94 +15,64 @@ npm run validate
34
15
  npm run serve
35
16
  ```
36
17
 
37
- Setup asks for the legal organization name, the initial policy owner and their email, a security reporting address, and the program timezone. It initializes Git when needed. The first local run then defines the initial service boundary and an optional program goal. A Type 2 choice records management intent, not an audit engagement. Completing onboarding opens Step 1 so you can add the real reviewers and operators, finish the oversight team, and confirm the criteria, commitments, vendors, and systems before moving on.
38
-
39
- Open the printed local URL. You can commit locally from Repository without configuring a remote. Add a remote when the team is ready to share the workspace, then the browser can pull with rebase and push reviewed commits.
40
-
41
- The creation summary reports the resolved engine version, program timezone, starter record counts, install result, and whether the target joined an existing Git worktree. Generated workspaces receive an organization-specific README with their engine version, validation commands, and remaining setup work.
18
+ Requires Node.js 20 or newer and Git.
42
19
 
43
20
  ## How it works
44
21
 
45
- 1. Confirm the program’s people and oversight team, applicable criteria, commitments, material vendors, and in-scope systems.
46
- 2. Review and activate the policies with a separate management reviewer, who is usually internal and may be external.
47
- 3. Tailor the starter controls, add each owner, actual procedure, scope, cadence, evidence source, and implementation date, and confirm any linked Work Queue schedules are enabled. Marking a control implemented starts eligible schedules. Then record any complementary customer or subservice controls.
48
- 4. Open each generated External Evidence draft, choose its authoritative source System, collect the named artifact, and have another person verify it.
49
- 5. Start the management candidate period, maintain risk assessments and risks, update controls when needed, work the filegrc queue, and preserve dated evidence.
50
- 6. Engage a CPA firm, record the separate firm-agreed period, review filegrc Evidence and External Evidence, prepare fieldwork, and generate the evidence packet.
51
-
52
- Long-form policies, procedures, plans, minutes, training, assertions, and audit responses are Markdown companions beside their JSON records. Screenshots, signed acknowledgements, reports, and fixed exports are attachments linked through evidence records.
22
+ The repository is the program. There is no separate application database.
53
23
 
54
- Third-party software is usually both a System and a Vendor. The application is the System because it operates controls and produces evidence. The provider is the Vendor because contracts, due diligence, and supplier risk belong to that relationship. Link the System to the Vendor with `vendorId`, and link exported evidence to the System.
24
+ - **JSON** holds records that filegrc validates, filters, and connects.
25
+ - **Markdown** holds policies, procedures, plans, minutes, and narratives.
26
+ - **Git** supplies authors, timestamps, revisions, diffs, and commit messages.
55
27
 
56
- ## Run the program
28
+ Use the same source through the local web app, a text editor, the CLI, or CI. Browser and CLI actions call the same rules, so engineers and agents see the same validation and readiness results.
57
29
 
58
- Use Overview to follow one six-step path: define scope, approve policies, implement controls, test External Evidence, operate the program, then complete the audit. Steps 1 through 4 and Step 6 open an overview with instructions, record links, progress, and completion status. Step 5 opens Policy Events and the Work Queue because operation is ongoing rather than a one-time checklist. The progress tracker opens the first incomplete step.
30
+ ## One path from setup to audit
59
31
 
60
32
  ![filegrc SOC 2 program overview](docs/filegrc-home.png)
61
33
 
62
- Use Work Queue for recurring work, Policy Event tasks, and other assigned follow-up. Trigger a Policy Event when the underlying change occurs, and filegrc adds its required actions to the queue with their owners and deadlines. Create a separate Action Item only when follow-up needs its own assignee, deadline, and completion proof. Each queue item shows its due window or deadline. Link dated proof to close the work.
34
+ 1. **Define scope.** Confirm owners, criteria, commitments, vendors, and in-scope systems.
35
+ 2. **Approve policies.** Tailor the proposals and record separate owners and reviewers.
36
+ 3. **Implement controls.** Add the real procedure, scope, cadence, and evidence source.
37
+ 4. **Test evidence collection.** Collect and verify evidence from each authoritative system.
38
+ 5. **Operate the program.** Work the queue, trigger Policy Events, maintain risks, and preserve dated evidence.
39
+ 6. **Audit.** Record the CPA engagement and agreed period, support fieldwork, and build the packet.
63
40
 
64
- Use the resource pages to maintain systems, people, vendors, risks, controls, tests, incidents, training, meetings, and External Evidence. The question-mark guide on each list explains what the record type is for, which policies call for it, and when to update it.
41
+ The Program Overview shows what is done, what is blocked, and what to do next.
65
42
 
66
- Agents use the same logic headlessly:
67
-
68
- ```sh
69
- npx filegrc guide risk-assessment --json
70
- npx filegrc program-path --json
71
- npx filegrc scaffold risk-assessment --title "2026 Annual Risk Assessment"
72
- npx filegrc list risk --json
73
- npx filegrc obligations --json
74
- npx filegrc program-readiness --summary --json
75
- npx filegrc complete obligation-id completion-record.json
76
- npx filegrc trigger person-started --occurred-on 2026-07-25 --subject person-id
77
- npx filegrc complete-action action-item-id completion-record.json --completed-on 2026-07-25
78
- npx filegrc complete-event obligation-event-id --completed-on 2026-07-25
79
- npx filegrc search "access review"
80
- ```
43
+ ## The routine work stays connected
81
44
 
82
- `program-path` reports the same six steps, current status, page order, exact Instructions, Use, Policy Basis, and next actions shown in the renderer. `program-readiness --summary --json` reports compact stage counts and next actions; omit `--summary` when you need every readiness item. `guide` reports that same page guidance for one resource, plus timing, required fields, valid values, relationship candidates, and Markdown locations. `scaffold` produces the same JSON and Markdown mutation shape used by the browser. Read `AGENTS.md` and `data/AGENTS.md` for the full headless workflow.
45
+ - **Work Queue** turns policy schedules and follow-up into upcoming, due, and overdue work.
46
+ - **Policy Events** create the right tasks for hiring, departures, incidents, vendor changes, and other events.
47
+ - **Program Readiness** checks whether management can begin a reliable evidence period.
48
+ - **Audit Readiness** checks the engagement, period, documents, evidence, and Type 2 populations.
49
+ - **Evidence packets** collect the scoped records, attachments, history, indexes, and checksums for delivery.
83
50
 
84
- ## Start the evidence period
85
-
86
- Program Readiness works without an audit ID or CPA firm:
87
-
88
- ```sh
89
- npx filegrc program-readiness --json
90
- npx filegrc program-readiness --require-ready
91
- ```
92
-
93
- The Evidence Ready gate requires defined scope, effective policies, implemented controls, configured authoritative systems, and verified collection for external evidence that does not already have a dedicated Step 5 record. Put scan reports, backup output, and other fixed artifacts in External Evidence records, then link them from the applicable Step 5 operating records. Starter obligations remain enabled proposals until their governing policies are effective and at least one linked control is implemented. A filegrc-managed control cannot be implemented while one of its linked Work Queue schedules is paused or waiting for policy approval.
94
-
95
- When the gate passes, record `candidatePeriodStart` on the workspace on the date reliable collection begins. This is management’s candidate Type 2 period. Do not backdate it. The later audit record keeps the separate period agreed with the CPA firm.
51
+ ![filegrc audit readiness](docs/filegrc-audit.png)
96
52
 
97
- ## Prepare the audit
53
+ Starter records connect policies, controls, owners, systems, evidence, and schedules. They are proposals, so review them against how your company actually works before approval.
98
54
 
99
- After engaging a CPA firm, create the audit record with the firm, scope, and exact agreed date or period. Audit Readiness checks the program foundation, engagement, formal scope and dates, management documents, filegrc Evidence, External Evidence, and Type 2 populations.
55
+ ## Built for engineers and agents
100
56
 
101
- ![filegrc audit readiness](docs/filegrc-audit.png)
102
-
103
- For a Type 2 audit, reconcile each complete period population to its authoritative system after the period closes. A zero-item population still needs its source export and query. filegrc Evidence consists of dated operating records and their Markdown and Git history. External Evidence consists of verified exports, reports, screenshots, signed files, and approved external references. The packet compiles both paths with the selected records, attachments, indexes, historical versions, and SHA-256 checksums.
57
+ The browser is helpful, but it is not required. An agent can discover the model, inspect valid relationships, create records, complete scheduled work, trigger events, and check the result from the CLI.
104
58
 
105
59
  ```sh
106
- npx filegrc prepare-audit audit-id
60
+ npx filegrc program-path --next --json
61
+ npx filegrc guide risk-assessment --json
62
+ npx filegrc obligations --json
63
+ npx filegrc program-readiness --summary --json
107
64
  npx filegrc audit-readiness audit-id --json
108
65
  npx filegrc evidence-packet --audit audit-id
109
66
  ```
110
67
 
111
- filegrc checks management preparation and packet integrity. The independent CPA firm still selects samples, tests controls, evaluates exceptions, decides whether evidence is sufficient, and issues the SOC 2 report.
112
-
113
- Early CPA engagement remains available when a customer deadline, unusual scope, or other timing risk needs input before the program reaches Evidence Ready. It is optional, not the default first action.
114
-
115
- ## What belongs elsewhere
116
-
117
- filegrc does not replace workforce, identity, source-control, deployment, infrastructure, monitoring, endpoint, backup, vulnerability, training, signature, procurement, contract, or vendor-risk systems.
68
+ Read `AGENTS.md` and `data/AGENTS.md` inside a generated workspace for the full headless workflow.
118
69
 
119
- Catalog each authoritative system in filegrc, record how to export from it, and attach or reference the fixed evidence when Audit Readiness asks for it. The generated external-delivery index identifies files that still need to be supplied through an auditor portal or another approved channel.
70
+ ## Clear boundaries
120
71
 
121
- The starter uses the SOC 2 Security category and does not include licensed criteria text. Add Availability, Processing Integrity, Confidentiality, or Privacy only when they are in scope.
72
+ filegrc manages GRC records and audit evidence. Your workforce, identity, source control, infrastructure, monitoring, endpoint, backup, training, signature, procurement, and vendor systems still operate the controls and produce source evidence.
122
73
 
123
- ## Repository safety
74
+ The independent CPA firm still selects samples, tests controls, evaluates exceptions, decides whether evidence is sufficient, and issues the SOC 2 report.
124
75
 
125
- The editable server has no authentication and binds to loopback by default. Do not expose it to an untrusted network. Use `npm run build` for a read-only site.
76
+ Do not put secrets or personal data that may need erasure into Git. The editable local server has no authentication and binds to loopback by default.
126
77
 
127
- Do not put secrets or personal data that may need erasure into Git. Read `AGENTS.md` before broad record changes or automation work.
78
+ Learn more at [filegrc.com](https://filegrc.com) or [view the source on GitHub](https://github.com/Sunpeak-AI/filegrc).
@@ -1,8 +1,8 @@
1
- # {{company_name}} SOC 2 Program
1
+ # {{program_title}}
2
2
 
3
- This private workspace holds {{company_name}}'s SOC 2 program records and audit evidence. JSON under `data/` stores structured records, Markdown stores long-form work, and Git records reviewed changes.
3
+ {{program_summary}}
4
4
 
5
- The workspace uses filegrc {{filegrc_version}} through the dependency range `{{filegrc_version_range}}`.
5
+ The workspace uses filegrc {{filegrc_version}} through the dependency spec `{{filegrc_version_range}}`.
6
6
 
7
7
  ## Work locally
8
8
 
@@ -20,22 +20,13 @@ Agents and terminal users can inspect the workspace without the browser:
20
20
 
21
21
  ```sh
22
22
  npx filegrc guide --json
23
- npx filegrc program-path --json
23
+ npx filegrc program-path --next --json
24
24
  npx filegrc obligations --json
25
25
  npx filegrc validate --json
26
26
  ```
27
27
 
28
28
  Read `AGENTS.md` and `data/AGENTS.md` before broad changes.
29
29
 
30
- ## Finish initial setup
31
-
32
- The starter policies, controls, and obligations are proposals. They do not state that {{company_name}} operates the described controls.
33
-
34
- 1. Run `npx filegrc setup` for guided service and goal setup, or use browser onboarding. Then finish Step 1 by adding the real reviewers and operators, finishing the oversight team, and confirming applicable criteria, commitments, material vendors, and in-scope systems.
35
- 2. Review the starter policies, appoint a reviewer who is separate from the policy owner, and activate only the policies that match current practice. The reviewer will usually be another person in the organization, but may be external.
36
- 3. Review the starter control set, implement each applicable control with its actual procedure, scope, cadence, evidence sources, and implementation date, and confirm any linked Work Queue schedules are enabled. Marking a control implemented starts eligible schedules. Then record any complementary customer or subservice controls.
37
- 4. Open each generated External Evidence draft, choose its authoritative source System, collect the named artifact, and have another person verify it.
38
- 5. Run `npx filegrc program-readiness --require-ready`, record the management candidate period start when reliable evidence collection begins, maintain risk assessments and risks, update controls when needed, use Work Queue for scheduled work, and trigger Policy Events when changes create required actions. `npx filegrc obligations` previews every event task, owner, deadline, and requested proof before the trigger creates anything.
39
- 6. Engage a CPA firm, record the separate firm-agreed period in an audit record, review filegrc Evidence and External Evidence, and prepare fieldwork.
30
+ {{starter_setup}}
40
31
 
41
32
  filegrc manages GRC records and audit evidence. It does not replace infrastructure logging, monitoring, identity, backup, endpoint, or incident-detection systems.
@@ -8,14 +8,14 @@ Treat the installed model as the authority. Do not infer a schema from a nearby
8
8
 
9
9
  ```sh
10
10
  npx filegrc guide --json
11
- npx filegrc program-path --json
11
+ npx filegrc program-path --next --json
12
12
  npx filegrc types --json
13
13
  npx filegrc guide RESOURCE_TYPE --json
14
14
  npx filegrc list RESOURCE_TYPE --json
15
15
  npx filegrc search "TERM" --json
16
16
  ```
17
17
 
18
- Use `program-path` to find the current lifecycle step and see the renderer’s exact page Instructions, Use, Policy Basis, commands, and next actions. Use `guide` before any unfamiliar create or status transition. It repeats the page guidance and reports required fields, fields required by a status, enum values, relationship types and candidates, Markdown slots, timing, and exact paths. Use `describe` only when you need the raw model definition.
18
+ Use `program-path --next --json` to find the current lifecycle step and first action. Use `--summary` for all six step statuses or `--current` for the current step’s full renderer Instructions, Use, Policy Basis, commands, and next actions. Use `guide` before any unfamiliar create or status transition. It repeats the page guidance and reports required fields, fields required by a status, enum values, relationship types and candidates, Markdown slots, timing, and exact paths. Use `describe` only when you need the raw model definition.
19
19
 
20
20
  ## Choose the right record
21
21
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  An evidence record explains what a proof item is, where it came from, what period it supports, who collected it, and which records or controls it supports. The attachment alone is not enough.
4
4
 
5
- Completing onboarding creates draft collection tests only for evidence that must come from systems outside filegrc and does not already have a dedicated Step 5 record. Risk assessments, meetings, vendor reviews, attestations, vulnerability scans, penetration tests, backup tests, exercises, exceptions, and findings do not need a separate test. When one of those operating records needs a fixed external artifact, create or update an External Evidence record for the artifact and link its ID from the operating record. Keep a generated collection test as `draft` until the artifact has actually been captured. Set it to `collected` only after selecting the source System, attaching or referencing the result, and recording the source, date, classification, and collector. Set it to `verified` only after another named person checks it.
5
+ Onboarding does not create collection tests. After confirming applicable controls and authoritative source Systems, preview proposed tests with `npx filegrc evidence-test-drafts --preview --json`, then explicitly create the missing drafts. Risk assessments, meetings, vendor reviews, attestations, vulnerability scans, penetration tests, backup tests, exercises, exceptions, and findings do not need a separate test. When one of those operating records needs a fixed external artifact, create or update an External Evidence record for the artifact and link its ID from the operating record. Keep a created collection test as `draft` until the artifact has actually been captured. Set it to `collected` only after selecting the source System, attaching or referencing the result, and recording the source, date, classification, and collector. Set it to `verified` only after another named person checks it.
6
6
 
7
7
  ## Create evidence
8
8
 
@@ -3,10 +3,10 @@
3
3
  "dataModelVersion": "1",
4
4
  "id": "workspace",
5
5
  "type": "workspace",
6
- "title": "{{company_name}} SOC 2 Program",
6
+ "title": "{{program_title}}",
7
7
  "organizationName": "{{company_name}}",
8
8
  "timezone": "{{timezone}}",
9
- "description": "SOC 2 Security program based on the AICPA Trust Services Criteria.",
9
+ "description": "{{program_description}}",
10
10
  "riskMethodology": {
11
11
  "method": "5x5 likelihood and impact",
12
12
  "likelihoodScale": ["Rare", "Unlikely", "Possible", "Likely", "Almost certain"],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "{{project_name}}",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "private": true,
5
5
  "description": "filegrc workspace for a SOC 2 program",
6
6
  "type": "module",
@@ -45,6 +45,38 @@
45
45
  {
46
46
  "key": "filegrc_version_range",
47
47
  "source": "resolved-filegrc-version"
48
+ },
49
+ {
50
+ "key": "program_title",
51
+ "source": "starter-profile"
52
+ },
53
+ {
54
+ "key": "program_description",
55
+ "source": "starter-profile"
56
+ },
57
+ {
58
+ "key": "program_summary",
59
+ "source": "starter-profile"
60
+ },
61
+ {
62
+ "key": "starter_setup",
63
+ "source": "starter-profile"
64
+ },
65
+ {
66
+ "key": "agent_title",
67
+ "source": "starter-profile"
68
+ },
69
+ {
70
+ "key": "agent_purpose",
71
+ "source": "starter-profile"
72
+ },
73
+ {
74
+ "key": "starter_baseline",
75
+ "source": "starter-profile"
76
+ },
77
+ {
78
+ "key": "audit_preparation_guidance",
79
+ "source": "starter-profile"
48
80
  }
49
81
  ]
50
82
  }