filegrc 0.3.3 → 0.4.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/src/index.js CHANGED
@@ -3,9 +3,9 @@ export { buildAgentGuide, findResourceReferences, listResourceTypes, scaffoldRes
3
3
  export { assessAuditPreparation, prepareAuditWorkspace } from "./audit-preparation.js";
4
4
  export { buildWorkspace } from "./build.js";
5
5
  export { generateEvidencePacket, prepareEvidencePacket, writeEvidencePacket } from "./evidence-packet.js";
6
- export { ensureEvidenceTestDrafts, planEvidenceTestDrafts } from "./evidence-tests.js";
7
6
  export {
8
7
  addEvidenceAttachment,
8
+ applyResourceBatch,
9
9
  createResource,
10
10
  createResourceAndLink,
11
11
  createResources,
@@ -18,14 +18,18 @@ export {
18
18
  export {
19
19
  commitAndPushWorkspace,
20
20
  commitWorkspace,
21
+ getBrowserRepositoryState,
21
22
  getFileHistory,
22
23
  getGitSummary,
23
24
  getWorkspaceHistories,
24
25
  pullWorkspace,
25
- pushWorkspace
26
+ pushWorkspace,
27
+ retryBrowserSync,
28
+ runBrowserMutation
26
29
  } from "./git.js";
27
30
  export { generateModelDocumentation } from "./model-docs.js";
28
31
  export { renderMarkdown } from "./markdown.js";
32
+ export { migrateModel, planModelMigration } from "./model-migration.js";
29
33
  export {
30
34
  completeObligationAction,
31
35
  completeObligationEvent,
@@ -33,11 +37,9 @@ export {
33
37
  createObligationEvent,
34
38
  planObligations
35
39
  } from "./obligations.js";
36
- export { assessProgramReadiness } from "./program-readiness.js";
40
+ export { assessEvidenceMap, assessProgramReadiness } from "./program-readiness.js";
37
41
  export {
38
42
  buildAgentProgramPath,
39
- policyEventName,
40
- POLICY_EVENT_NAMES,
41
43
  PROGRAM_PATH,
42
44
  RESOURCE_INSTRUCTIONS,
43
45
  resourceProgramContext
@@ -50,9 +52,10 @@ export {
50
52
  nextCalendarOccurrence
51
53
  } from "./recurrence.js";
52
54
  export { searchResources, searchableValues } from "./search.js";
55
+ export { effectiveResourceStatus } from "./resource-status.js";
53
56
  export { createFilegrcServer, serveWorkspace } from "./server.js";
54
57
  export { normalizeSetupPayload, planWorkspaceSetup, setupWorkspace, summarizeSetupResult } from "./setup.js";
55
- export { createAppState } from "./state.js";
58
+ export { createAppState, createResourceDetail } from "./state.js";
56
59
  export { currentCalendarDate, formatCalendarDate, formatLocalDateTime } from "./time.js";
57
60
  export { validateWorkspace } from "./validate.js";
58
61
  export { indexResources, loadWorkspace } from "./workspace.js";
package/src/model-docs.js CHANGED
@@ -15,7 +15,7 @@ export function generateModelDocumentation(model) {
15
15
  "",
16
16
  "## Program path",
17
17
  "",
18
- "The renderer, CLI, generated agent instructions, and this reference use the same six-step lifecycle:",
18
+ "The renderer, CLI, generated agent instructions, and this reference use the same five-step lifecycle:",
19
19
  "",
20
20
  ...programPath.flatMap((stage) => [
21
21
  `### Step ${stage.number}. ${stage.title}`,
@@ -35,6 +35,52 @@ export function generateModelDocumentation(model) {
35
35
  ...stage.commands.map((command) => `- \`${command}\``),
36
36
  ""
37
37
  ]),
38
+ "## Relation groups",
39
+ "",
40
+ "Relationship fields use the named groups below. The registry expands each group to explicit resource types, so no relationship accepts an unrestricted wildcard.",
41
+ "",
42
+ "| Group | Resource types |",
43
+ "| --- | --- |",
44
+ ...Object.entries(model.relationGroups || {}).map(([name, types]) => (
45
+ `| \`${name}\` | ${types.map((type) => `\`${type}\``).join(", ")} |`
46
+ )),
47
+ "",
48
+ "## Relationship constraints",
49
+ "",
50
+ "Relationship constraints prevent cycles and duplicate active authority or access records.",
51
+ "",
52
+ ...((model.relationshipConstraints?.acyclic || []).map((constraint) => (
53
+ `- \`${constraint.resourceType}.${constraint.field}\` must be acyclic.`
54
+ ))),
55
+ ...((model.relationshipConstraints?.unique || []).map((constraint) => (
56
+ `- \`${constraint.resourceType}\` records in ${constraint.statuses.map((status) => `\`${status}\``).join(", ")} must be unique by ${constraint.fields.map((field) => `\`${field}\``).join(", ")}.`
57
+ ))),
58
+ "",
59
+ "## Obligation activities",
60
+ "",
61
+ "The model owns each activity name, allowed recurrence modes and scope types, its default completion record, and every record type that can prove completion.",
62
+ "",
63
+ "| Activity | Title | Recurrence | Scope resource types | Default completion | Accepted completion records |",
64
+ "| --- | --- | --- | --- | --- | --- |",
65
+ ...Object.entries(model.obligationActivities || {}).map(([name, activity]) => (
66
+ `| \`${name}\` | ${escapeCell(activity.title)} | ${activity.recurrenceModes.map((mode) => `\`${mode}\``).join(", ")} | ${activity.scopeResourceTypes.map((type) => `\`${type}\``).join(", ")} | \`${activity.completionType}\` | ${activity.completionResourceTypes.map((type) => `\`${type}\``).join(", ")} |`
67
+ )),
68
+ "",
69
+ "## Policy events",
70
+ "",
71
+ "The model owns each event title and the minimum and maximum count for each subject resource type.",
72
+ "",
73
+ "| Event | Title | Subject rules |",
74
+ "| --- | --- | --- |",
75
+ ...Object.entries(model.policyEvents || {}).map(([name, event]) => (
76
+ `| \`${name}\` | ${escapeCell(event.title)} | ${event.subjectRules.map(({ resourceType, minimum = 0, maximum }) => `\`${resourceType}\` ${minimum}..${Number.isInteger(maximum) ? maximum : "*"}`).join(", ")} |`
77
+ )),
78
+ "",
79
+ "## Nested object schemas",
80
+ "",
81
+ "Named object schemas reject unknown keys unless the schema explicitly allows a typed map or arbitrary JSON. Conditional properties are valid only for the selected discriminator.",
82
+ "",
83
+ ...Object.entries(model.objectTypes || {}).flatMap(([name, schema]) => objectTypeDocumentation(name, schema)),
38
84
  "## Common fields",
39
85
  "",
40
86
  "| Field | Type | Required | Meaning |",
@@ -53,7 +99,7 @@ export function generateModelDocumentation(model) {
53
99
  "",
54
100
  "## Program and audit readiness defaults",
55
101
  "",
56
- "Program Readiness checks management scope, policy adoption, control implementation, authoritative source configuration, and verified test captures without requiring an audit record. Audit Readiness starts after a CPA firm is engaged and uses the defaults below to prepare Type 1 and Type 2 fieldwork.",
102
+ "Program Readiness checks management scope, policy adoption, control implementation, and authoritative evidence mapping without requiring an audit record. Audit Readiness starts after a CPA firm is engaged and uses the defaults below to prepare Type 1 and Type 2 fieldwork.",
57
103
  "",
58
104
  "Management documents:",
59
105
  "",
@@ -66,9 +112,9 @@ export function generateModelDocumentation(model) {
66
112
  "Authoritative systems of record:",
67
113
  "",
68
114
  ...model.evidenceSourceFamilies.map((item) => (
69
- item.collectionTestRequired === false
70
- ? `- **${item.title}** (${item.sourceKinds.map((kind) => `\`${kind}\``).join(", ")}): ${item.description} filegrc operating records: ${item.operationRecordTypes.map((type) => `\`${type}\``).join(", ")}. No separate collection test is required. ${item.timing}`
71
- : `- **${item.title}** (${item.sourceKinds.map((kind) => `\`${kind}\``).join(", ")}): ${item.description} Test external collection: ${item.testPrompt} ${item.timing}`
115
+ item.filegrcManaged === true
116
+ ? `- **${item.title}** (${item.sourceKinds.map((kind) => `\`${kind}\``).join(", ")}): ${item.description} FileGRC operating records: ${item.operationRecordTypes.map((type) => `\`${type}\``).join(", ")}. Expected evidence: ${item.evidencePrompt} ${item.timing}`
117
+ : `- **${item.title}** (${item.sourceKinds.map((kind) => `\`${kind}\``).join(", ")}): ${item.description} Expected evidence: ${item.evidencePrompt} ${item.timing}`
72
118
  )),
73
119
  "",
74
120
  "## Resource groups",
@@ -143,7 +189,12 @@ function choiceLabel(name) {
143
189
  }
144
190
 
145
191
  function fieldType(field) {
146
- if (field.type === "array") return `array of ${field.items ?? "values"}`;
192
+ if (field.type === "array") {
193
+ return field.itemObjectType
194
+ ? `array of object (\`${field.itemObjectType}\`)`
195
+ : `array of ${field.items ?? "values"}`;
196
+ }
197
+ if (field.type === "object" && field.objectType) return `object (\`${field.objectType}\`)`;
147
198
  return field.format && field.format !== field.type ? `${field.type} (${field.format})` : field.type;
148
199
  }
149
200
 
@@ -151,14 +202,44 @@ function fieldNotes(field) {
151
202
  return [
152
203
  field.label,
153
204
  field.values ? `Values: ${field.values.map((item) => `\`${item}\``).join(", ")}` : "",
205
+ field.registry ? `Values come from the \`${field.registry}\` registry.` : "",
206
+ field.relationGroup ? `Relation group: \`${field.relationGroup}\`.` : "",
154
207
  field.relation ? `References: ${field.relation.map((item) => `\`${item}\``).join(", ")}` : "",
155
208
  field.minimum !== undefined ? `Minimum: \`${field.minimum}\`.` : "",
156
209
  field.maximum !== undefined ? `Maximum: \`${field.maximum}\`.` : "",
210
+ field.managed ? "Managed by filegrc." : "",
157
211
  field.disjointFrom ? `Must not overlap \`${field.disjointFrom}\`.` : "",
158
- field.requiredWhen ? `Required when ${Object.entries(field.requiredWhen).map(([key, value]) => `\`${key}\` is \`${value}\``).join(" and ")}` : ""
212
+ field.requiredWhen ? `Required when ${conditionText(field.requiredWhen)}.` : "",
213
+ field.allowedWhen ? `Allowed when ${conditionText(field.allowedWhen)}.` : ""
159
214
  ].filter(Boolean).join(" ");
160
215
  }
161
216
 
217
+ function objectTypeDocumentation(name, schema) {
218
+ const lines = [`### \`${name}\``, ""];
219
+ const properties = Object.entries(schema.properties || {});
220
+ if (properties.length) {
221
+ const required = new Set(schema.required || []);
222
+ lines.push("| Property | Type | Required | Notes |", "| --- | --- | --- | --- |");
223
+ for (const [propertyName, property] of properties) {
224
+ const requiredLabel = required.has(propertyName)
225
+ ? "Yes"
226
+ : property.requiredWhen
227
+ ? "Conditional"
228
+ : "No";
229
+ lines.push(`| \`${propertyName}\` | ${fieldType(property)} | ${requiredLabel} | ${escapeCell(fieldNotes(property))} |`);
230
+ }
231
+ } else if (schema.additionalProperties === true) {
232
+ lines.push("Allows arbitrary JSON properties.");
233
+ } else if (schema.additionalProperties) {
234
+ lines.push(`Allows dynamic keys whose values are ${fieldType(schema.additionalProperties)}.`);
235
+ } else {
236
+ lines.push("Does not allow properties.");
237
+ }
238
+ if (schema.keyFormat) lines.push("", `Key format: \`${schema.keyFormat}\`.`);
239
+ lines.push("");
240
+ return lines;
241
+ }
242
+
162
243
  function escapeCell(value) {
163
244
  return String(value).replaceAll("|", "\\|").replaceAll("\n", " ");
164
245
  }