filegrc 0.2.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.
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,10 +1,10 @@
1
- # FileGRC
1
+ # filegrc
2
2
 
3
- FileGRC is a zero-dependency Node.js engine for Git-native GRC workspaces. It validates structured JSON records and their Markdown companions, renders a local web app, provides safe CRUD operations, and builds a read-only audit view.
3
+ filegrc is a zero-dependency Node.js engine for Git-native GRC workspaces. It validates structured JSON records and their Markdown companions, renders a local web app, provides safe CRUD operations, and builds a read-only audit view.
4
4
 
5
5
  Program Readiness checks management-owned scope, policy adoption, control implementation, authoritative source configuration, and verified test captures without requiring an audit record. Audit Readiness starts after CPA engagement and checks the firm-agreed date or period, engagement-specific management documents, operating evidence, and Type 2 population completeness.
6
6
 
7
- Controls linked to FileGRC obligations show whether their Work Queue schedules are waiting for policy approval, ready for implementation, running, paused, or mixed. Marking a fully configured control implemented starts its enabled schedules when their governing policies are effective.
7
+ Controls linked to filegrc obligations show whether their Work Queue schedules are waiting for policy approval, ready for implementation, running, paused, or mixed. Marking a fully configured control implemented starts its enabled schedules when their governing policies are effective.
8
8
 
9
9
  Most users should create a complete workspace:
10
10
 
@@ -27,6 +27,7 @@ npx filegrc references risk-example --json
27
27
  npx filegrc describe risk
28
28
  npx filegrc search "access review"
29
29
  npx filegrc evidence-test-drafts
30
+ npx filegrc program-readiness --summary --json
30
31
  npx filegrc program-readiness --require-ready
31
32
  npx filegrc audit-readiness audit-id
32
33
  npx filegrc prepare-audit audit-id
@@ -35,17 +36,17 @@ npx filegrc evidence-packet --start 2026-01-01 --end 2026-06-30 --audit audit-id
35
36
 
36
37
  `filegrc serve --help` prints bind, port, environment, and safety options without starting the server. The editable server defaults to `127.0.0.1:8787`; set `FILEGRC_HOST`, `FILEGRC_PORT`, or the matching flags when needed.
37
38
 
38
- `filegrc setup` provides the headless equivalent of browser onboarding. It accepts all initial service-boundary fields and a management program goal as flags or a JSON payload. Selecting Type 1 or Type 2 updates the workspace goal and program scope. It does not create an audit record.
39
+ `filegrc setup` provides the headless equivalent of browser onboarding. Run it without arguments for guided terminal setup, or pass all initial service-boundary fields and a management program goal as flags or a JSON payload. Selecting Type 1 or Type 2 updates the workspace goal and program scope. It does not create an audit record.
39
40
 
40
41
  `filegrc program-path` gives agents the renderer’s six-step order, exact page Instructions, Use, Policy Basis, commands, current state, and next actions. `filegrc guide <type>` repeats the matching page guidance and adds fields, relationship candidates, Markdown slots, and timing for that record type.
41
42
 
42
- `filegrc program-readiness` reports whether management can start a candidate Type 2 period. Use `--require-ready` in automation. The command does not require an audit ID or CPA firm.
43
+ `filegrc program-readiness` reports whether management can start a candidate Type 2 period. Add `--summary --json` for compact stage counts and next actions, or omit `--summary` for every readiness item. Use `--require-ready` in automation. The command does not require an audit ID or CPA firm.
43
44
 
44
45
  `filegrc evidence-test-drafts` creates missing draft tests for external evidence that does not already have a dedicated Step 5 record. When a Step 5 operating record exists, put the fixed artifact in an External Evidence record and link it from that operating record. Completing browser or CLI onboarding runs the same operation automatically.
45
46
 
46
47
  `filegrc obligations` shows recurring work and a task-level preview for each Policy Event, including owners, deadlines, and requested proof. `filegrc trigger` adds the event and all of its Action Items to the Work Queue atomically, then prints the created task IDs and deadlines.
47
48
 
48
- Long-form Markdown lives beside its JSON record. FileGRC derives the Markdown path, so records do not store it.
49
+ Long-form Markdown lives beside its JSON record. filegrc derives the Markdown path, so records do not store it.
49
50
 
50
51
  Headless creates and updates accept either a record or `{ "record": {...}, "content": {...} }`, the same mutation shape used by the web app. Run `filegrc get <id> --mutation` before an update to include JSON and Markdown revision hashes; stale writes are rejected. Use `filegrc content <type> <id>` to read a companion and `--write <file|->` to replace it. `filegrc guide --json` is the compact action and resource index for agents.
51
52
 
@@ -53,7 +54,7 @@ Use `filegrc attach <evidence-id> <source-file>` to copy a fixed evidence file u
53
54
 
54
55
  Use `filegrc detach <evidence-id> <attachment-name> --yes` for explicit removal. Evidence records with linked local attachments cannot be deleted.
55
56
 
56
- The package requires Node.js 20 or newer. It uses Git for authors, commit timestamps, messages, diffs, and revisions. Browser commits are explicit. Without a remote they remain local; with a remote the browser commits and pushes together. Browser pulls use rebase. Agents and terminal users use Git directly; the FileGRC CLI does not wrap pull, commit, or push.
57
+ The package requires Node.js 20 or newer. It uses Git for authors, commit timestamps, messages, diffs, and revisions. Browser commits are explicit. Without a remote they remain local; with a remote the browser commits and pushes together. Browser pulls use rebase. Agents and terminal users use Git directly; the filegrc CLI does not wrap pull, commit, or push.
57
58
 
58
59
  The editable server has no authentication and binds to loopback by default. Put it behind trusted authentication before exposing it on a network, or publish the read-only static build.
59
60
 
package/model/v1.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "modelVersion": "1",
3
- "title": "FileGRC data model",
3
+ "title": "filegrc data model",
4
4
  "description": "Stable, query-worthy GRC metadata. Long-form work is stored as implicit Markdown companion files beside each structured JSON record.",
5
5
  "recordContent": {
6
6
  "slot": "record",
@@ -29,19 +29,19 @@
29
29
  },
30
30
  "evidenceSourceFamilies": [
31
31
  {"id":"workforce","title":"Workforce System","sourceKinds":["workforce"],"controlCodes":["HR-01","HR-03","IAM-03"],"testEvidenceKind":"test-export","testPrompt":"Export a workforce-change report from the HR or workforce system containing starters, role changes, and departures.","description":"Catalog the HR or workforce system that is authoritative for starts, role changes, and departures. Bring the complete workforce-change population and source reports used to reconcile access and responsibilities.","timing":"Identify the source during scoping. Preserve event records as changes occur and export the complete Type 2 population after the period closes."},
32
- {"id":"training-acknowledgement","title":"Training and Acknowledgements","sourceKinds":["training-acknowledgement"],"controlCodes":["GOV-03","HR-01","HR-02"],"testEvidenceKind":"test-export","testPrompt":"Export assignments and completions tied to a specific training or policy revision.","collectionTestRequired":false,"operationRecordTypes":["training","attestation"],"description":"FileGRC records training assignments, content revisions, completions, acknowledgements, exceptions, and overdue follow-up during Step 5.","timing":"Set up training and acknowledgement records before assignments begin. Preserve completion proof as work occurs, then reconcile the complete Type 2 population to the workforce population after close."},
32
+ {"id":"training-acknowledgement","title":"Training and Acknowledgements","sourceKinds":["training-acknowledgement"],"controlCodes":["GOV-03","HR-01","HR-02"],"testEvidenceKind":"test-export","testPrompt":"Export assignments and completions tied to a specific training or policy revision.","collectionTestRequired":false,"operationRecordTypes":["training","attestation"],"description":"filegrc records training assignments, content revisions, completions, acknowledgements, exceptions, and overdue follow-up during Step 5.","timing":"Set up training and acknowledgement records before assignments begin. Preserve completion proof as work occurs, then reconcile the complete Type 2 population to the workforce population after close."},
33
33
  {"id":"identity-access","title":"Identity and Access Systems","sourceKinds":["identity-access"],"controlCodes":["IAM-01","IAM-02","IAM-03"],"testEvidenceKind":"test-export","testPrompt":"Export users, roles, privileged access, or authentication settings from the identity provider or an in-scope application.","description":"Catalog the identity provider and each important application that enforces access. Bring identity, role, privileged-access, authentication-setting, review, and removal exports.","timing":"Identify sources during scoping. Capture configuration near the Type 1 date or at the start and end of a Type 2 period; export complete change and review populations after the Type 2 period closes."},
34
34
  {"id":"production-change","title":"Production Change Systems","sourceKinds":["production-change"],"controlCodes":["CHG-01"],"testEvidenceKind":"test-capture","testPrompt":"Capture a change from the source control or deployment system showing review, testing, approval, deployment, and rollback information.","description":"Catalog source control, deployment, and infrastructure-change systems. Bring protection settings, reviews, test and approval records, deployments, emergency changes, and rollback evidence.","timing":"Identify sources before the audit period. Preserve per-change evidence as changes occur and export the complete period population after a Type 2 period closes."},
35
- {"id":"security-monitoring","title":"Security Monitoring Systems","sourceKinds":["security-monitoring"],"controlCodes":["LOG-01","IR-01","IR-02"],"testEvidenceKind":"test-capture","testPrompt":"Capture logging or alert configuration and a delivered test alert from the monitoring system.","description":"Catalog logging and alerting systems. Bring configuration, coverage, alert delivery tests, alerts, investigations, and zero-event proof. FileGRC records qualifying incidents and their response during Step 5.","timing":"Capture configuration and coverage at the Type 1 date or across the Type 2 period. Preserve cases as they occur and export complete alert and incident populations after close."},
36
- {"id":"vulnerability-management","title":"Vulnerability Management","sourceKinds":["vulnerability-management"],"controlCodes":["VUL-01","PEN-01"],"testEvidenceKind":"test-export","testPrompt":"Export a result from the vulnerability or dependency scanner showing scope, coverage, findings, and severity.","collectionTestRequired":false,"operationRecordTypes":["vulnerability-scan","penetration-test"],"description":"FileGRC records vulnerability scans and penetration tests during Step 5. Put the scanner output or independent report in an External Evidence record and link it from the operating record rather than creating a separate collection test.","timing":"Confirm coverage before the audit period. Preserve scan and remediation evidence when generated and export the complete Type 2 population after close."},
37
- {"id":"endpoint-asset","title":"Endpoint Management Systems","sourceKinds":["endpoint-asset"],"controlCodes":["PHY-01","OPS-01","OPS-02","DATA-01"],"testEvidenceKind":"test-export","testPrompt":"Export devices, security configuration, and compliance status from the endpoint-management system.","description":"Catalog device-management and endpoint-compliance systems. Bring the complete device population, security configuration, compliance status, and exceptions. FileGRC records asset ownership, custody, loss, return, and disposal during Step 5.","timing":"Identify sources before devices receive access. Capture configuration near the Type 1 date or across the Type 2 period and export the complete Type 2 population after close."},
38
- {"id":"backup-recovery","title":"Backup and Recovery","sourceKinds":["backup-recovery"],"controlCodes":["BCP-01","BCP-02"],"testEvidenceKind":"test-export","testPrompt":"Export job history from the backup system showing scheduled runs, successes, failures, and follow-up.","collectionTestRequired":false,"operationRecordTypes":["backup-test","exercise"],"description":"FileGRC records restoration tests and continuity exercises during Step 5. Put backup-system output in an External Evidence record and link it from the operating record rather than creating a separate collection test.","timing":"Capture configuration at the Type 1 date or across the Type 2 period. Preserve restoration and exercise results when performed and export complete job and failure history after period close."},
39
- {"id":"vendor-management","title":"Vendors","sourceKinds":["vendor-management"],"controlCodes":["VEN-01","VEN-02"],"testEvidenceKind":"test-capture","testPrompt":"Capture a completed vendor review with its contract, assurance report, risk decision, and follow-up.","collectionTestRequired":false,"operationRecordTypes":["vendor-review"],"description":"FileGRC records vendor reviews, risk decisions, supporting reports, exceptions, and follow-up during Step 5.","timing":"Identify relevant subservice organizations during scoping. Complete reviews during operation, obtain current assurance reports before fieldwork, and document bridge coverage when a report ends before the report period."},
40
- {"id":"exception-finding","title":"Exceptions and Findings","sourceKinds":["exception-finding"],"controlCodes":["MON-01","EXC-01"],"testEvidenceKind":"test-export","testPrompt":"Export open and closed exceptions or findings with owners, status, dates, and remediation verification.","collectionTestRequired":false,"operationRecordTypes":["exception","finding","action-item"],"description":"FileGRC records control exceptions, findings, risk acceptance, remediation, verification, and overdue work during Step 5.","timing":"Record items as they arise. Keep their status and follow-up current, then include the complete period population in fieldwork."},
35
+ {"id":"security-monitoring","title":"Security Monitoring Systems","sourceKinds":["security-monitoring"],"controlCodes":["LOG-01","IR-01","IR-02"],"testEvidenceKind":"test-capture","testPrompt":"Capture logging or alert configuration and a delivered test alert from the monitoring system.","description":"Catalog logging and alerting systems. Bring configuration, coverage, alert delivery tests, alerts, investigations, and zero-event proof. filegrc records qualifying incidents and their response during Step 5.","timing":"Capture configuration and coverage at the Type 1 date or across the Type 2 period. Preserve cases as they occur and export complete alert and incident populations after close."},
36
+ {"id":"vulnerability-management","title":"Vulnerability Management","sourceKinds":["vulnerability-management"],"controlCodes":["VUL-01","PEN-01"],"testEvidenceKind":"test-export","testPrompt":"Export a result from the vulnerability or dependency scanner showing scope, coverage, findings, and severity.","collectionTestRequired":false,"operationRecordTypes":["vulnerability-scan","penetration-test"],"description":"filegrc records vulnerability scans and penetration tests during Step 5. Put the scanner output or independent report in an External Evidence record and link it from the operating record rather than creating a separate collection test.","timing":"Confirm coverage before the audit period. Preserve scan and remediation evidence when generated and export the complete Type 2 population after close."},
37
+ {"id":"endpoint-asset","title":"Endpoint Management Systems","sourceKinds":["endpoint-asset"],"controlCodes":["PHY-01","OPS-01","OPS-02","DATA-01"],"testEvidenceKind":"test-export","testPrompt":"Export devices, security configuration, and compliance status from the endpoint-management system.","description":"Catalog device-management and endpoint-compliance systems. Bring the complete device population, security configuration, compliance status, and exceptions. filegrc records asset ownership, custody, loss, return, and disposal during Step 5.","timing":"Identify sources before devices receive access. Capture configuration near the Type 1 date or across the Type 2 period and export the complete Type 2 population after close."},
38
+ {"id":"backup-recovery","title":"Backup and Recovery","sourceKinds":["backup-recovery"],"controlCodes":["BCP-01","BCP-02"],"testEvidenceKind":"test-export","testPrompt":"Export job history from the backup system showing scheduled runs, successes, failures, and follow-up.","collectionTestRequired":false,"operationRecordTypes":["backup-test","exercise"],"description":"filegrc records restoration tests and continuity exercises during Step 5. Put backup-system output in an External Evidence record and link it from the operating record rather than creating a separate collection test.","timing":"Capture configuration at the Type 1 date or across the Type 2 period. Preserve restoration and exercise results when performed and export complete job and failure history after period close."},
39
+ {"id":"vendor-management","title":"Vendors","sourceKinds":["vendor-management"],"controlCodes":["VEN-01","VEN-02"],"testEvidenceKind":"test-capture","testPrompt":"Capture a completed vendor review with its contract, assurance report, risk decision, and follow-up.","collectionTestRequired":false,"operationRecordTypes":["vendor-review"],"description":"filegrc records vendor reviews, risk decisions, supporting reports, exceptions, and follow-up during Step 5.","timing":"Identify relevant subservice organizations during scoping. Complete reviews during operation, obtain current assurance reports before fieldwork, and document bridge coverage when a report ends before the report period."},
40
+ {"id":"exception-finding","title":"Exceptions and Findings","sourceKinds":["exception-finding"],"controlCodes":["MON-01","EXC-01"],"testEvidenceKind":"test-export","testPrompt":"Export open and closed exceptions or findings with owners, status, dates, and remediation verification.","collectionTestRequired":false,"operationRecordTypes":["exception","finding","action-item"],"description":"filegrc records control exceptions, findings, risk acceptance, remediation, verification, and overdue work during Step 5.","timing":"Record items as they arise. Keep their status and follow-up current, then include the complete period population in fieldwork."},
41
41
  {"id":"data-handling","title":"Data Protection Configuration","sourceKinds":["data-handling"],"controlCodes":["DATA-02","DATA-03"],"testEvidenceKind":"test-capture","testPrompt":"Capture encryption and retention settings from an in-scope System.","description":"Catalog the systems authoritative for encryption settings and retention rules. Bring current configuration and approved schedules. Preserve completed disposal actions, exceptions, and verification during Step 5.","timing":"Confirm configuration and retention rules before the audit period. Preserve disposal evidence when work occurs and capture configuration near the Type 1 date or across the Type 2 period."},
42
42
  {"id":"network-security","title":"Network Security Systems","sourceKinds":["network-security"],"controlCodes":["NET-01"],"testEvidenceKind":"test-capture","testPrompt":"Capture current firewall or network-access rules from the network system and confirm that remote and production access paths appear.","description":"Catalog the systems authoritative for network boundaries, firewall rules, and remote access. Bring current configuration, rule reviews, approvals, changes, and exceptions.","timing":"Confirm boundary and remote-access configuration before the audit period. Preserve rule changes as they occur and capture current configuration near the Type 1 date or across the Type 2 period."},
43
- {"id":"governance","title":"Governance","sourceKinds":["governance"],"controlCodes":["GOV-01","GOV-02"],"testEvidenceKind":"test-capture","testPrompt":"Capture one completed oversight review or policy approval with the participants, decisions, dates, and follow-up work.","collectionTestRequired":false,"operationRecordTypes":["policy-review","meeting"],"description":"FileGRC records policy approvals, oversight reviews, meetings, decisions, assigned actions, and completion proof in Steps 2 and 5.","timing":"Approve policies before they take effect. Preserve oversight records and decisions when the work occurs."},
44
- {"id":"risk-management","title":"Risk Management","sourceKinds":["risk-management"],"controlCodes":["RSK-01"],"testEvidenceKind":"test-export","testPrompt":"Export the risk register with owners, ratings, treatment decisions, review dates, and open actions.","collectionTestRequired":false,"operationRecordTypes":["risk-assessment","risk"],"description":"FileGRC records risk assessments, risks, treatment decisions, reviews, and follow-up during Step 5.","timing":"Complete the initial assessment while operating the program. Preserve risk changes and reviews as they occur, then include the current register in fieldwork."}
43
+ {"id":"governance","title":"Governance","sourceKinds":["governance"],"controlCodes":["GOV-01","GOV-02"],"testEvidenceKind":"test-capture","testPrompt":"Capture one completed oversight review or policy approval with the participants, decisions, dates, and follow-up work.","collectionTestRequired":false,"operationRecordTypes":["policy-review","meeting"],"description":"filegrc records policy approvals, oversight reviews, meetings, decisions, assigned actions, and completion proof in Steps 2 and 5.","timing":"Approve policies before they take effect. Preserve oversight records and decisions when the work occurs."},
44
+ {"id":"risk-management","title":"Risk Management","sourceKinds":["risk-management"],"controlCodes":["RSK-01"],"testEvidenceKind":"test-export","testPrompt":"Export the risk register with owners, ratings, treatment decisions, review dates, and open actions.","collectionTestRequired":false,"operationRecordTypes":["risk-assessment","risk"],"description":"filegrc records risk assessments, risks, treatment decisions, reviews, and follow-up during Step 5.","timing":"Complete the initial assessment while operating the program. Preserve risk changes and reviews as they occur, then include the current register in fieldwork."}
45
45
  ],
46
46
  "commonFields": {
47
47
  "schemaVersion": {"type":"integer","required":true,"const":1,"label":"Schema version"},
@@ -106,7 +106,7 @@
106
106
  "singleton": "renderer.json",
107
107
  "description": "Optional local interface settings, including onboarding visibility and manually completed Step pages. Renderer settings are not required for SOC 2 and do not change compliance records.",
108
108
  "guidance": {
109
- "policyBasis": "Renderer settings are a FileGRC convenience, not a SOC 2 requirement, control, audit record, or substitute for evidence.",
109
+ "policyBasis": "Renderer settings are a filegrc convenience, not a SOC 2 requirement, control, audit record, or substitute for evidence.",
110
110
  "cadence": "Change it when the team wants to rerun or suppress an optional renderer workflow, then review and commit the resulting diff."
111
111
  },
112
112
  "required": ["showOnboarding"],
@@ -262,7 +262,7 @@
262
262
  "pluralTitle": "Documents",
263
263
  "group": "governance",
264
264
  "collection": "documents",
265
- "description": "Governed plans, charters, procedures, standards, reports, and templates that are not Policies or another FileGRC record type. A general document catalog is not required for SOC 2.",
265
+ "description": "Governed plans, charters, procedures, standards, reports, and templates that are not Policies or another filegrc record type. A general document catalog is not required for SOC 2.",
266
266
  "guidance": {
267
267
  "policyBasis": "Policies rely on governed documents for detailed plans, procedures, charters, and reports. Git preserves the approved text and its revision history.",
268
268
  "cadence": "Follow each record's review cadence. Starter governed documents are reviewed at least annually and after material changes or use, with an approver who is separate from the owner.",
@@ -274,7 +274,7 @@
274
274
  "status": {"type":"enum","values":["draft","active","superseded","retired"],"filter":true},
275
275
  "documentKind": {"type":"string","filter":true},
276
276
  "template": {"type":"boolean","filter":true},
277
- "approverIds": {"type":"array","items":"id","relation":["person","team"],"disjointFrom":"ownerIds"},
277
+ "approverIds": {"type":"array","items":"id","relation":["person","team"],"disjointFrom":"ownerIds","requiredWhen":{"status":"active"}},
278
278
  "version": {"type":"string"},
279
279
  "effectiveOn": {"type":"date","requiredWhen":{"status":"active"}},
280
280
  "approvedOn": {"type":"date","requiredWhen":{"status":"active"}},
@@ -299,10 +299,10 @@
299
299
  "group": "evidence",
300
300
  "collection": "evidence",
301
301
  "recordPath": "{id}/evidence.json",
302
- "description": "External Evidence holds exports, reports, screenshots, signed files, and approved external references collected from other Systems. Step 5 records created in FileGRC do not need a separate record here. Step 6 reviews and packages both evidence paths for the CPA firm.",
302
+ "description": "External Evidence holds exports, reports, screenshots, signed files, and approved external references collected from other Systems. Step 5 records created in filegrc do not need a separate record here. Step 6 reviews and packages both evidence paths for the CPA firm.",
303
303
  "guidance": {
304
- "policyBasis": "The information security and data handling policies require retained proof from authoritative Systems when FileGRC's own operating records do not contain the full result.",
305
- "cadence": "Before the candidate period begins, test each selected control family that relies on evidence from outside FileGRC. Once operation starts, collect external evidence whenever the control runs, keep FileGRC operating records current, verify evidence before audit use, cover the stated period, and retain it according to classification and record rules.",
304
+ "policyBasis": "The information security and data handling policies require retained proof from authoritative Systems when filegrc's own operating records do not contain the full result.",
305
+ "cadence": "Before the candidate period begins, test each selected control family that relies on evidence from outside filegrc. Once operation starts, collect external evidence whenever the control runs, keep filegrc operating records current, verify evidence before audit use, cover the stated period, and retain it according to classification and record rules.",
306
306
  "sourceResourceIds": ["policy-information-security","policy-data-protection-handling"]
307
307
  },
308
308
  "required": ["status","evidenceKind"],
@@ -351,7 +351,7 @@
351
351
  "collection": "obligations",
352
352
  "description": "Reusable schedules for recurring or event-driven work. Obligations feed the Work Queue; completion records and linked Evidence prove that the work occurred. Obligations are not required for SOC 2.",
353
353
  "guidance": {
354
- "policyBasis": "FileGRC uses Obligations to turn policy and control cadence into owned, dated work linked to its scope and required proof.",
354
+ "policyBasis": "filegrc uses Obligations to turn policy and control cadence into owned, dated work linked to its scope and required proof.",
355
355
  "cadence": "Treat starter work as proposed until every governing policy is active and effective and, when the obligation names controls, at least one linked control is implemented. Then use the recurrence and activation date, and create a separate completion record for every period.",
356
356
  "sourceResourceIds": ["policy-information-security","policy-data-protection-handling","document-business-continuity-disaster-recovery"]
357
357
  },
@@ -406,7 +406,7 @@
406
406
  "collection": "frameworks",
407
407
  "description": "Published criteria sets and versions used to define the program and audit scope.",
408
408
  "guidance": {
409
- "policyBasis": "The CPA examination uses the selected Framework. FileGRC’s starter references provide orientation but do not replace the publisher’s official criteria.",
409
+ "policyBasis": "The CPA examination uses the selected Framework. filegrc’s starter references provide orientation but do not replace the publisher’s official criteria.",
410
410
  "cadence": "Add or retire a version only through a deliberate scope decision. Reconfirm the selected version when planning each audit."
411
411
  },
412
412
  "required": ["status","version"],
@@ -503,7 +503,7 @@
503
503
  "description": "Management’s actual safeguards and procedures, mapped to Policies, Requirements, Systems, and evidence sources. The Work Queue schedules recurring operation where configured; Evidence shows that operation occurred.",
504
504
  "guidance": {
505
505
  "policyBasis": "Controls translate approved Policies and applicable Requirements into owned procedures that management can operate and prove. Policy text alone does not show implementation.",
506
- "cadence": "Before marking a control implemented, record its owner, actual procedure in Record Markdown, system scope, cadence, authoritative evidence sources, and implementation date. FileGRC-managed controls also require enabled schedules with effective governing policies. Marking the control implemented starts eligible schedules.",
506
+ "cadence": "Before marking a control implemented, record its owner, actual procedure in Record Markdown, system scope, cadence, authoritative evidence sources, and implementation date. filegrc-managed controls also require enabled schedules with effective governing policies. Marking the control implemented starts eligible schedules.",
507
507
  "sourceResourceIds": ["policy-information-security","policy-data-protection-handling"],
508
508
  "obligationActivityTypes": ["risk-assessment","policy-review"]
509
509
  },
@@ -678,10 +678,10 @@
678
678
  "sourceResourceIds": ["policy-information-security"],
679
679
  "obligationActivityTypes": ["policy-review"]
680
680
  },
681
- "required": ["status","ownerIds","approverIds"],
681
+ "required": ["status","ownerIds"],
682
682
  "fields": {
683
683
  "status": {"type":"enum","values":["draft","in-review","approved","active","superseded","retired"],"filter":true},
684
- "approverIds": {"type":"array","items":"id","relation":["person","team"],"disjointFrom":"ownerIds"},
684
+ "approverIds": {"type":"array","items":"id","relation":["person","team"],"disjointFrom":"ownerIds","requiredWhen":{"status":["in-review","approved","active"]}},
685
685
  "policyNumber": {"type":"string","search":true},
686
686
  "policyKind": {"type":"string","filter":true},
687
687
  "version": {"type":"string"},
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "filegrc",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Zero-dependency Git-native GRC engine",
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": {
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { createResource, createResources, deleteResource, updateResource } from "./files.js";
4
4
  import { createResourceId } from "./id.js";
5
+ import { partiesIndependent } from "./parties.js";
5
6
  import { resolveDataPath } from "./paths.js";
6
7
  import { assessProgramReadiness } from "./program-readiness.js";
7
8
  import { markdownEntries } from "./resource-markdown.js";
@@ -550,7 +551,7 @@ function evidenceStage(audit, records, byId, model) {
550
551
  const evidenceFamiliesFor = (control) => (model.evidenceSourceFamilies || []).filter((family) => (
551
552
  (family.controlCodes || []).includes(control.code)
552
553
  ));
553
- const fileGRCRecords = records.filter((record) => (
554
+ const filegrcRecords = records.filter((record) => (
554
555
  !NON_EVIDENCE_RECORD_TYPES.has(record.type)
555
556
  && controlIdsForRecord(record, byId).size
556
557
  && recordRelevantToAuditDate(record, audit, model)
@@ -558,7 +559,7 @@ function evidenceStage(audit, records, byId, model) {
558
559
  const managedControls = controls.filter((control) => managedFamilies.some((family) => (
559
560
  (family.controlCodes || []).includes(control.code)
560
561
  )));
561
- const controlsWithFileGRCRecords = managedControls.filter((control) => fileGRCRecords.some((record) => (
562
+ const controlsWithFilegrcRecords = managedControls.filter((control) => filegrcRecords.some((record) => (
562
563
  controlIdsForRecord(record, byId).has(control.id)
563
564
  )));
564
565
  const externalControls = controls.filter((control) => externalFamilies.some((family) => (
@@ -570,12 +571,12 @@ function evidenceStage(audit, records, byId, model) {
570
571
  const items = [
571
572
  item(
572
573
  "filegrc-evidence",
573
- managedControls.length && controlsWithFileGRCRecords.length === managedControls.length ? "complete" : managedControls.length ? "action" : "info",
574
- "Review FileGRC Evidence",
574
+ managedControls.length && controlsWithFilegrcRecords.length === managedControls.length ? "complete" : managedControls.length ? "action" : "info",
575
+ "Review filegrc Evidence",
575
576
  managedControls.length
576
- ? `${controlsWithFileGRCRecords.length} of ${managedControls.length} selected controls that use FileGRC workflows have a dated operating record for the formal period. Complete each Step 5 record, link it to the control, and add results in its structured fields or Markdown.`
577
- : "No selected controls use a dedicated FileGRC operating record.",
578
- fileGRCRecords[0] || { type: managedFamilies[0]?.operationRecordTypes?.[0] || "control" }
577
+ ? `${controlsWithFilegrcRecords.length} of ${managedControls.length} selected controls that use filegrc workflows have a dated operating record for the formal period. Complete each Step 5 record, link it to the control, and add results in its structured fields or Markdown.`
578
+ : "No selected controls use a dedicated filegrc operating record.",
579
+ filegrcRecords[0] || { type: managedFamilies[0]?.operationRecordTypes?.[0] || "control" }
579
580
  ),
580
581
  item(
581
582
  "external-evidence",
@@ -600,7 +601,7 @@ function evidenceStage(audit, records, byId, model) {
600
601
  continue;
601
602
  }
602
603
  if (source.collectionTestRequired === false) {
603
- const sourceRecords = fileGRCRecords.filter((record) => (
604
+ const sourceRecords = filegrcRecords.filter((record) => (
604
605
  relevantControls.some((control) => controlIdsForRecord(record, byId).has(control.id))
605
606
  ));
606
607
  const coveredControls = relevantControls.filter((control) => sourceRecords.some((record) => (
@@ -611,7 +612,7 @@ function evidenceStage(audit, records, byId, model) {
611
612
  coveredControls.length === relevantControls.length ? "complete" : "action",
612
613
  source.title,
613
614
  coveredControls.length === relevantControls.length
614
- ? `${sourceRecords.length} dated FileGRC ${sourceRecords.length === 1 ? "record" : "records"} cover ${relevantControls.length} mapped controls. External artifacts needed to support those results are linked from the operating records.`
615
+ ? `${sourceRecords.length} dated filegrc ${sourceRecords.length === 1 ? "record" : "records"} cover ${relevantControls.length} mapped controls. External artifacts needed to support those results are linked from the operating records.`
615
616
  : `${coveredControls.length} of ${relevantControls.length} mapped controls have a dated ${source.operationRecordTypes.map(displayValue).join(" or ")} record for the formal period. Complete the Step 5 work and attach or reference any supporting external artifact on that record.`,
616
617
  sourceRecords[0] || { type: source.operationRecordTypes[0] }
617
618
  ));
@@ -640,7 +641,7 @@ function evidenceStage(audit, records, byId, model) {
640
641
  || { type: "system" }
641
642
  ));
642
643
  }
643
- return stage("evidence", "Audit Evidence", "Review both evidence paths: dated FileGRC operating records and verified External Evidence from authoritative systems. FileGRC includes both in the audit packet.", items);
644
+ return stage("evidence", "Audit Evidence", "Review both evidence paths: dated filegrc operating records and verified External Evidence from authoritative systems. filegrc includes both in the audit packet.", items);
644
645
  }
645
646
 
646
647
  function populationsStage(audit, records, byId, model) {
@@ -676,11 +677,11 @@ function populationsStage(audit, records, byId, model) {
676
677
  }
677
678
 
678
679
  function auditorStage() {
679
- return stage("auditor", "Fieldwork and Report", "FileGRC prepares the record set but does not make the CPA firm's independent judgments.", [
680
+ return stage("auditor", "Fieldwork and Report", "filegrc prepares the record set but does not make the CPA firm's independent judgments.", [
680
681
  item("firm-eligibility", "external", "Firm eligibility and independence", "Confirm directly with the engagement partner that the firm and signing practitioner meet applicable licensing, peer-review, ethics, and independence requirements. Keep the signed engagement terms with the audit record if management needs a copy."),
681
682
  item("sampling", "external", "Sample selection and independent testing", "The auditor chooses samples, performs tests, evaluates exceptions, and decides whether more work is needed."),
682
683
  item("report", "external", "Report and opinion", "Management reviews and signs its representations. The auditor issues the final report and opinion."),
683
- item("criteria", "external", "Authoritative criteria and examination guidance", "FileGRC stores reference IDs and orientation text. Use the publisher's current official criteria and the engagement team's examination guidance for scope, evaluation, and reporting.")
684
+ item("criteria", "external", "Authoritative criteria and examination guidance", "filegrc stores reference IDs and orientation text. Use the publisher's current official criteria and the engagement team's examination guidance for scope, evaluation, and reporting.")
684
685
  ]);
685
686
  }
686
687
 
@@ -946,30 +947,6 @@ function escapeRegExp(value) {
946
947
  return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
947
948
  }
948
949
 
949
- function partiesIndependent(ownerIds, approverIds, byId) {
950
- const owners = partyPeople(ownerIds, byId);
951
- const approvers = partyPeople(approverIds, byId);
952
- return owners.size > 0
953
- && approvers.size > 0
954
- && ![...owners].some((id) => approvers.has(id));
955
- }
956
-
957
- function partyPeople(ids = [], byId, seen = new Set()) {
958
- const people = new Set();
959
- for (const id of ids) {
960
- if (seen.has(id)) continue;
961
- seen.add(id);
962
- const record = byId.get(id);
963
- if (record?.type === "person") people.add(id);
964
- if (record?.type === "team") {
965
- for (const personId of partyPeople([...(record.memberIds || []), ...(record.chairIds || [])], byId, seen)) {
966
- people.add(personId);
967
- }
968
- }
969
- }
970
- return people;
971
- }
972
-
973
950
  function auditSummary(audit) {
974
951
  return {
975
952
  id: audit.id,
package/src/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { readFile, writeFile } from "node:fs/promises";
2
2
  import { resolve } from "node:path";
3
+ import { createInterface } from "node:readline/promises";
3
4
  import { loadModel } from "../model/index.js";
4
5
  import { buildAgentGuide, findResourceReferences, listResourceTypes, scaffoldResourceMutation } from "./agent.js";
5
6
  import { assessAuditPreparation, prepareAuditWorkspace } from "./audit-preparation.js";
@@ -42,6 +43,7 @@ const BOOLEAN_FLAGS = new Set([
42
43
  "mutation",
43
44
  "preview",
44
45
  "require-ready",
46
+ "summary",
45
47
  "write-docs",
46
48
  "yes"
47
49
  ]);
@@ -60,7 +62,7 @@ export async function runCli(argv = process.argv.slice(2)) {
60
62
  host: flags.host ?? process.env.FILEGRC_HOST,
61
63
  port: flags.port ?? process.env.FILEGRC_PORT
62
64
  });
63
- console.log(`FileGRC workspace: ${result.url}`);
65
+ console.log(`filegrc workspace: ${result.url}`);
64
66
  console.log(`Data: ${result.root}/data`);
65
67
  return await new Promise((resolvePromise) => {
66
68
  const stop = () => result.server.close(resolvePromise);
@@ -70,7 +72,7 @@ export async function runCli(argv = process.argv.slice(2)) {
70
72
  }
71
73
  if (command === "setup") {
72
74
  const payload = positionals[0] ? await readSetupPayload(positionals[0]) : {};
73
- const result = await setupWorkspace(root, {
75
+ const setupInput = await completeInteractiveSetup(root, {
74
76
  ...payload,
75
77
  ...(flags["service-name"] !== undefined ? { serviceName: flags["service-name"] } : {}),
76
78
  ...(flags.boundary !== undefined ? { boundary: flags.boundary } : {}),
@@ -81,6 +83,7 @@ export async function runCli(argv = process.argv.slice(2)) {
81
83
  ...(flags["program-goal"] !== undefined ? { programGoal: flags["program-goal"] } : {}),
82
84
  ...(flags.draft ? { draft: true } : {})
83
85
  });
86
+ const result = await setupWorkspace(root, setupInput);
84
87
  if (flags.json) console.log(JSON.stringify(result, null, 2));
85
88
  else {
86
89
  console.log(`${result.draft ? "Saved draft scope" : "Completed initial setup"} for ${result.system.title}.`);
@@ -226,7 +229,15 @@ export async function runCli(argv = process.argv.slice(2)) {
226
229
  if (command === "program-readiness") {
227
230
  const loaded = await loadWorkspace(root);
228
231
  const result = await assessProgramReadiness(loaded, { asOf: flags["as-of"] });
229
- if (flags.json) console.log(JSON.stringify(result, null, 2));
232
+ const output = flags.summary ? summarizeProgramReadiness(result) : result;
233
+ if (flags.json) console.log(JSON.stringify(output, null, 2));
234
+ else if (flags.summary) {
235
+ console.log(`${result.status.toUpperCase()}: ${result.progress.complete} of ${result.progress.total} program items complete`);
236
+ for (const stage of output.stages) {
237
+ console.log(`${stage.status.toUpperCase()}\t${stage.title}\t${stage.counts.action} actions`);
238
+ }
239
+ if (output.firstAction) console.log(`Next: ${output.firstAction.title}\t${output.firstAction.message}`);
240
+ }
230
241
  else {
231
242
  console.log(`${result.status.toUpperCase()}: ${result.progress.complete} of ${result.progress.total} program items complete`);
232
243
  console.log(`${result.target.label}${result.target.candidatePeriodStart ? `, candidate period starts ${result.target.candidatePeriodStart}` : ""}`);
@@ -239,7 +250,7 @@ export async function runCli(argv = process.argv.slice(2)) {
239
250
  }
240
251
  }
241
252
  if (flags["require-ready"] && !result.evidenceReady) process.exitCode = 2;
242
- return result;
253
+ return output;
243
254
  }
244
255
  if (command === "evidence-test-drafts") {
245
256
  const result = await ensureEvidenceTestDrafts(root);
@@ -554,6 +565,56 @@ async function readSetupPayload(path) {
554
565
  return parsed;
555
566
  }
556
567
 
568
+ async function completeInteractiveSetup(root, payload) {
569
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return payload;
570
+ const loaded = await loadWorkspace(root);
571
+ const activePeople = loaded.resources.filter(({ type, status }) => type === "person" && status === "active");
572
+ if (!activePeople.length) throw new Error("Setup requires at least one active person who can own the service.");
573
+ const classifications = Object.keys(loaded.workspace.classificationDefinitions || {});
574
+ const prompt = createInterface({ input: process.stdin, output: process.stdout });
575
+ const result = { ...payload };
576
+ const askRequired = async (label, defaultValue = "") => {
577
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
578
+ let value = "";
579
+ while (!value) value = (await prompt.question(`${label}${suffix}: `)).trim() || defaultValue;
580
+ return value;
581
+ };
582
+ const askChoice = async (label, choices, defaultValue = "") => {
583
+ let value = "";
584
+ while (!choices.includes(value)) {
585
+ value = await askRequired(`${label} (${choices.join("/")})`, defaultValue);
586
+ }
587
+ return value;
588
+ };
589
+ try {
590
+ result.serviceName ||= await askRequired(
591
+ "Service name",
592
+ loaded.workspace.organizationName ? `${loaded.workspace.organizationName} service` : ""
593
+ );
594
+ result.boundary ||= await askRequired("Service boundary");
595
+ result.ownerId ||= await askChoice(
596
+ "Service owner ID",
597
+ activePeople.map(({ id }) => id),
598
+ activePeople[0].id
599
+ );
600
+ result.criticality ||= await askChoice("Criticality", ["low", "medium", "high", "critical"], "high");
601
+ result.dataClassification ||= classifications.length
602
+ ? await askChoice(
603
+ "Data classification",
604
+ classifications,
605
+ classifications.includes("Confidential") ? "Confidential" : classifications[0]
606
+ )
607
+ : await askRequired("Data classification");
608
+ if (result.internetExposed === undefined) {
609
+ result.internetExposed = (await askChoice("Internet exposed", ["yes", "no"])) === "yes";
610
+ }
611
+ result.programGoal ||= await askChoice("Program goal", ["none", "readiness", "type-1", "type-2"]);
612
+ } finally {
613
+ prompt.close();
614
+ }
615
+ return result;
616
+ }
617
+
557
618
  async function readTextInput(path) {
558
619
  if (path === true || !path) throw new Error("Pass --write <markdown-file|->.");
559
620
  return path === "-" ? readStdin() : readFile(resolve(String(path)), "utf8");
@@ -576,7 +637,7 @@ async function printVersion() {
576
637
  }
577
638
 
578
639
  function printHelp() {
579
- console.log(`FileGRC - Git-native GRC workspace
640
+ console.log(`filegrc - Git-native GRC workspace
580
641
 
581
642
  Usage:
582
643
  filegrc serve [root] [--host 127.0.0.1] [--port 8787]
@@ -592,7 +653,7 @@ Usage:
592
653
  filegrc list [resource-type] [--json]
593
654
  filegrc search <query> [--type resource-type] [--json]
594
655
  filegrc obligations [--as-of YYYY-MM-DD] [--from YYYY-MM-DD] [--through YYYY-MM-DD] [--now RFC3339] [--complete] [--json]
595
- filegrc program-readiness [--as-of YYYY-MM-DD] [--require-ready] [--json]
656
+ filegrc program-readiness [--as-of YYYY-MM-DD] [--require-ready] [--summary] [--json]
596
657
  filegrc evidence-test-drafts [--json]
597
658
  filegrc audit-readiness [audit-id] [--require-ready] [--json]
598
659
  filegrc prepare-audit <audit-id> [--json]
@@ -634,7 +695,8 @@ Safety:
634
695
  filegrc setup [setup.json|-] [options]
635
696
 
636
697
  Create or update the initial service boundary through the same validated operation
637
- used by browser onboarding. JSON keys use the camelCase forms shown below.
698
+ used by browser onboarding. Run without input in an interactive terminal for guided
699
+ setup. JSON keys use the camelCase forms shown below.
638
700
 
639
701
  Options:
640
702
  --service-name <name> serviceName
@@ -662,6 +724,7 @@ is required.
662
724
  Options:
663
725
  --as-of <date> Evaluate effective dates and obligations on YYYY-MM-DD
664
726
  --require-ready Exit with code 2 unless the Evidence Ready gate passes
727
+ --summary Omit item details and print stage counts and next actions
665
728
  --json Print the result as JSON
666
729
  --root <path> Workspace path
667
730
  --help Show this help`);
@@ -860,6 +923,41 @@ function printProgramPath(result) {
860
923
  }
861
924
  }
862
925
 
926
+ function summarizeProgramReadiness(result) {
927
+ const summarizeItem = (item) => item ? {
928
+ id: item.id,
929
+ status: item.status,
930
+ title: item.title,
931
+ message: item.message,
932
+ ...(item.resourceType ? { resourceType: item.resourceType } : {}),
933
+ ...(item.resourceId ? { resourceId: item.resourceId } : {})
934
+ } : null;
935
+ return {
936
+ schemaVersion: result.schemaVersion,
937
+ generatedAt: result.generatedAt,
938
+ asOf: result.asOf,
939
+ status: result.status,
940
+ evidenceReady: result.evidenceReady,
941
+ operating: result.operating,
942
+ canStartCandidatePeriod: result.canStartCandidatePeriod,
943
+ suggestedCandidatePeriodStart: result.suggestedCandidatePeriodStart,
944
+ target: result.target,
945
+ progress: result.progress,
946
+ counts: result.counts,
947
+ scopeCounts: Object.fromEntries(
948
+ Object.entries(result.scope).map(([name, ids]) => [name.replace(/Ids$/, ""), ids.length])
949
+ ),
950
+ firstAction: summarizeItem(result.firstAction),
951
+ stages: result.stages.map((stage) => ({
952
+ id: stage.id,
953
+ title: stage.title,
954
+ status: stage.status,
955
+ counts: stage.counts,
956
+ firstAction: summarizeItem(stage.items.find(({ status }) => status === "action"))
957
+ }))
958
+ };
959
+ }
960
+
863
961
  function eventWindowText(window) {
864
962
  if (Number.isInteger(window?.endOffsetHours)) {
865
963
  return window.endOffsetHours === 0 ? "due at event time" : `due within ${window.endOffsetHours} hours`;