Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -779,3 +779,5 @@ Each Program has an exact-revision Obligation Collection Review. Its complete po
Schedule-set and Control collection reviewer eligibility distinguish Obligation queue ownership from personal work assignments. Team membership and chairing a Team-owned review queue do not establish that a Person operates the Controls. Control ownership still resolves Team members and Appointment holders, and personal or Appointment assignments on Obligations remain conflicts. Governed content approval keeps its full owner separation checks, including Team membership. A reviewer who also owns implementation work remains ineligible.

Readiness keeps shared prerequisites separate from individual implementation work. A Program schedule-set approval is one action with its selected Controls as dependents. Each Control retains the approval check and cannot pass the readiness gate until it clears, but otherwise complete implementation checks do not produce another action or reduce implementation progress. Mapped evidence-source setup is also one action per source; missing Control mappings remain individual gaps. Workflow findings, recommendations, progress, CLI output, and browser checklists share these bindings. Source-family items remain evidence-map rollups rather than duplicate setup actions.

Core owns runtime compatibility for hosted and local use. Stable pre-1.0 versions share a minor compatibility line; stable 1.0 and later versions share a major line. Runtime compatibility assessment exposes installed and repository versions, supported line, workspace model support, API contract support, and a named failure reason. CLI, server startup, previews, mutations, and workspace evaluation use this policy. Version files remain authoritative and are never rewritten by compatibility checks. Missing or malformed managed lockfiles and unresolved linked dependencies fail closed. Explicit model migrations retain their unsupported-model preview path. Releases within a line must preserve both existing record reads and generated record interchange; a breaking format or contract belongs in a new line.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "filegrc-monorepo",
"version": "0.17.3",
"version": "0.17.4",
"private": true,
"workspaces": [
"packages/filegrc",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-filegrc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-filegrc",
"version": "0.17.3",
"version": "0.17.4",
"description": "Create a filegrc workspace for a SOC 2 program",
"license": "MIT",
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions packages/create-filegrc/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -524,13 +524,13 @@ async function runCombinedSetup(target, input) {
async function writeMinimalLockfile(target, name, versionRange) {
const lock = {
name,
version: "0.17.3",
version: "0.17.4",
lockfileVersion: 3,
requires: true,
packages: {
"": {
name,
version: "0.17.3",
version: "0.17.4",
dependencies: { filegrc: versionRange }
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/create-filegrc/template/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Use standards terms only when their meanings match. Do not call a general progra

If the installed CLI reports that this workspace uses an unsupported model, run the exact `migrate --to-model ... --preview --json` command in its error. Review the preview’s automatic, review-required, and unsupported classifications before applying that version with `--yes`. Repeat one version at a time until the workspace reaches the installed model version.

After changing the installed `filegrc` version, run `npm ci` and validate the workspace. FileGRC rejects CLI, server, and write operations when the installed version differs from `package-lock.json`. Older calculated revision bindings remain valid when the reviewed facts have not changed; do not repeat a management review solely because the engine changed.
After changing the installed `filegrc` version, run `npm ci` and validate the workspace. FileGRC accepts stable installed and locked versions in the same compatibility line, minor before 1.0 and major from 1.0 onward, subject to supported workspace models and API contracts. Hosted adapters can inspect `assessWorkspaceRuntimeCompatibility` and deploy one tested patch per line. Reject unsupported lines, prereleases, and malformed version files; repair local installs with `npm ci` or choose a supported hosted runtime. Do not rewrite customer manifests, lockfiles, or workspace versions to bypass these checks. Older calculated revision bindings remain valid when the reviewed facts have not changed; do not repeat a management review solely because the engine changed.

The [model v11 upgrade guide](https://github.com/Alignbase/filegrc/blob/main/docs/upgrading-to-model-v11.md) explains owner-recorded Control implementation and periodic Control collection oversight. The [model v10 upgrade guide](https://github.com/Alignbase/filegrc/blob/main/docs/upgrading-to-model-v10.md) explains Reporting Channel Sets and the legacy-route review.

Expand Down
2 changes: 1 addition & 1 deletion packages/create-filegrc/template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ npm run serve

Requires Node.js 20 or newer and Git.

Run FileGRC through the version installed by `npm ci`. The CLI, local server, and write APIs stop when that installed version differs from `package-lock.json`. Older revision bindings remain readable after an engine upgrade, so an unchanged review does not need to be repeated or rewritten just to adopt a new engine.
Run FileGRC through the version installed by `npm ci`. The CLI, local server, and write APIs accept stable versions in the same compatibility line, minor before 1.0 and major from 1.0 onward, subject to workspace model and API support. Hosted applications can run one tested patch in that line without changing repository version files. Unsupported lines, prereleases, and malformed versions are rejected. Older revision bindings remain readable after an engine upgrade, so an unchanged review does not need to be repeated or rewritten just to adopt a new engine.

Existing model v10 workspaces must run `npx filegrc migrate --to-model 11 --preview --json` after installing a model v11 package. The migration removes the old per-Control implementation review fields. Owners can record implementation directly, while periodic Control oversight uses a Collection Review. Independent Policy, Document, and Training approvals do not change. Older workspaces migrate one model version at a time. See the [model v11 upgrade guide](https://github.com/Alignbase/filegrc/blob/main/docs/upgrading-to-model-v11.md).

Expand Down
2 changes: 1 addition & 1 deletion packages/create-filegrc/template/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "{{project_name}}",
"version": "0.17.3",
"version": "0.17.4",
"private": true,
"description": "filegrc workspace for a SOC 2 program",
"type": "module",
Expand Down
21 changes: 16 additions & 5 deletions packages/create-filegrc/test/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ test("creates a complete generic repository with one dependency", async (context
policyOwnerEmail: "owner@example.test",
securityContactEmail: "security@example.test",
timezone: "America/Chicago",
filegrcVersion: "1.2.3",
filegrcVersion: "0.17.2",
install: false,
effectiveDate: "2026-07-25"
});
assert.equal(result.engineVersion, "1.2.3");
assert.equal(result.engineVersion, "0.17.2");
assert.deepEqual(result.resourceCounts, {
total: 224,
byType: {
Expand Down Expand Up @@ -72,12 +72,12 @@ test("creates a complete generic repository with one dependency", async (context
assert.ok(proposedWork.triggers.length > 0);
assert.equal(proposedWork.items.every(({ status }) => status === "proposed"), true);
const packageJson = JSON.parse(await readFile(join(target, "package.json"), "utf8"));
assert.deepEqual(packageJson.dependencies, { filegrc: "^1.2.3" });
assert.deepEqual(packageJson.dependencies, { filegrc: "^0.17.2" });
assert.equal(packageJson.private, true);
const readme = await readFile(join(target, "README.md"), "utf8");
assert.equal(readme.includes("{{"), false);
assert.match(readme, /# Example "Engineering" SOC 2 Program/);
assert.match(readme, /FileGRC 1\.2\.3/);
assert.match(readme, /FileGRC 0\.17\.2/);
assert.match(readme, /npx filegrc setup/);
assert.match(readme, /npx filegrc program-path --next --json/);
assert.match(readme, /finish Step 1 by adding the real reviewers and operators, finishing the oversight team/);
Expand Down Expand Up @@ -627,6 +627,12 @@ test("creates a complete generic repository with one dependency", async (context
assert.match(ignoredOutput, /packet\.json/);
const gitRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd: target, encoding: "utf8" }).trim();
assert.equal(await realpath(gitRoot), await realpath(target));
const incomplete = await validateWorkspace(target);
assert.equal(incomplete.diagnostics.some(({ code }) => code === "runtime-compatibility"), true);
const installedLockPath = join(target, "package-lock.json");
const installedLock = JSON.parse(await readFile(installedLockPath, "utf8"));
installedLock.packages["node_modules/filegrc"] = { version: "0.17.2" };
await writeFile(installedLockPath, JSON.stringify(installedLock));
const validation = await validateWorkspace(target);
assert.deepEqual(validation.counts, { resources: 224, errors: 0, warnings: 3 });
assert.equal(
Expand Down Expand Up @@ -820,7 +826,7 @@ test("creates a thirteen-record foundation without selecting a framework", async
target,
yes: true,
starter: "foundation",
filegrcVersion: "1.2.3",
filegrcVersion: "0.17.2",
install: false
});
assert.equal(result.starter, "foundation");
Expand Down Expand Up @@ -851,6 +857,11 @@ test("creates a thirteen-record foundation without selecting a framework", async
assert.match(agents, /No framework or assurance program has been selected yet/);
assert.match(agents, /does not include framework requirements, policies, governed documents/);
assert.doesNotMatch(agents, /The generated workspace starts with the SOC 2 Security category/);
assert.equal((await validateWorkspace(target)).ok, false, "skipped installation leaves an unresolved engine lock");
const lockPath = join(target, "package-lock.json");
const lock = JSON.parse(await readFile(lockPath, "utf8"));
lock.packages["node_modules/filegrc"] = { version: "0.17.2" };
await writeFile(lockPath, JSON.stringify(lock));
assert.equal((await validateWorkspace(target)).ok, true);
assert.equal(
(await readdir(join(target, "data", "policies"))).some((name) => name.endsWith(".json")),
Expand Down
10 changes: 10 additions & 0 deletions packages/filegrc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,13 @@ Core builds dashboard links as `https://app.filegrc.com/connections/<encoded con
Track this file in Git. Existing repositories that ignore `.filegrc/` should use `.filegrc/*` and add `!.filegrc/hosted-automation.json`. Person mappings and delivery policy remain separate files.

Model v12 requires a complete Program Obligation schedule-set review before activation. Use `npx filegrc review-collection obligation --program PROGRAM_ID --scaffold`, record adopted rules, replacements, retired duplicates, and intentionally inapplicable proposals, then preview and save the exact revision with an independent reviewer. Reporting Channel Sets need independent approval in Step 1; Step 3 verifies their effective coverage and usability at cutover. Existing model v11 workspaces preview `npx filegrc migrate --to-model 12 --preview --json`. See the [model v12 upgrade guide](https://github.com/Alignbase/filegrc/blob/main/docs/upgrading-to-model-v12.md).

## Hosted runtime compatibility

A hosted application can run one tested stable patch per supported compatibility line. Before 1.0, the line is the minor version, such as `0.17.x`. From 1.0 onward, it is the major version, such as `1.x`. Core accepts stable repository versions in its own line, including both older and newer patches, while retaining workspace model, record validation, and API contract checks. Prereleases, malformed versions, and other lines are rejected. Build metadata does not change the line. Diagnostic views remain available for model errors, with failed validation; ordinary mutations stay blocked.

Use `assessWorkspaceRuntimeCompatibility(root, { requiredApiContractVersion: 1 })` before presenting hosted operations. It returns `compatible`, `installedVersion`, `repositoryVersion`, `supportedLine`, `repositoryLine`, `dataModelVersion`, `supportedModelVersions`, `apiContractVersion`, and a nullable `reason`. Failures include a repair message. `workspaceEngineVersion` remains the raw lockfile reader; `assertWorkspaceEngineVersion` uses the compatibility policy and attaches the result to `error.compatibility` when rejecting an operation. Unmanaged workspaces may omit package files; package-managed workspaces still require a readable lockfile with a resolved version, including linked dependency targets.

Choose a tested runtime in the repository's line that supports its model and the adapter's API contract. For a local installation mismatch, run `npm ci`. Repair missing or malformed package files through normal dependency installation. Preview explicit migrations for unsupported older workspace models. Do not rewrite customer manifests, lockfiles, or workspace versions to bypass the guard. Published runtimes predating this policy retain their original exact-version mutation guard, so hosted adapters must deploy a runtime containing this change.

Patch releases must preserve readable records and generated records across their compatibility line. An incompatible record format, model requirement, or API contract needs a new compatibility line and an explicit upgrade path, rather than relaxed validation.
2 changes: 1 addition & 1 deletion packages/filegrc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "filegrc",
"version": "0.17.3",
"version": "0.17.4",
"description": "Zero-dependency Git-native GRC engine",
"license": "MIT",
"repository": {
Expand Down
5 changes: 4 additions & 1 deletion packages/filegrc/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ export async function runCli(argv = process.argv.slice(2)) {
const targetRoot = ["serve", "build", "validate"].includes(command)
? positionals[0] ?? root
: root;
assertWorkspaceEngineVersion(targetRoot);
assertWorkspaceEngineVersion(targetRoot, {
allowModelMigration: command === "migrate",
allowModelDiagnostics: ["validate", "build", "serve"].includes(command)
});

if (command === "automation") {
const result = await assessHostedAutomation(resolveWorkspaceRoot(root));
Expand Down
2 changes: 1 addition & 1 deletion packages/filegrc/src/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export async function applyRetentionScheduleReviewBatch(input, changes) {
export async function applyModelMigrationBatch(input, changes) {
return serializeWorkspaceMutation(input, (root) => (
applyResourceBatchUnlocked(root, changes, "model-migration")
));
), { allowModelMigration: true });
}

async function applyResourceBatchUnlocked(input, changes = {}, lifecycleOperation = null) {
Expand Down
2 changes: 1 addition & 1 deletion packages/filegrc/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export { createFilegrcServer, serveWorkspace } from "./server.js";
export { normalizeSetupPayload, planWorkspaceSetup, setupWorkspace, summarizeSetupResult } from "./setup.js";
export { createAppState, createResourceDetail } from "./state.js";
export { currentCalendarDate, formatCalendarDate, formatLocalDateTime } from "./time.js";
export { assertWorkspaceEngineVersion, workspaceEngineVersion } from "./runtime-version.js";
export { assertWorkspaceEngineVersion, workspaceEngineVersion, assessWorkspaceRuntimeCompatibility, assessRuntimeCompatibility, runtimeCompatibilityLine, RUNTIME_API_CONTRACT_VERSION } from "./runtime-version.js";
export { calculatedRevisionDiagnostic, REVISION_SCHEME_VERSION } from "./revisions.js";
export { validateWorkspace } from "./validate.js";
export {
Expand Down
4 changes: 2 additions & 2 deletions packages/filegrc/src/mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const mutationQueues = new Map();
const activeMutation = new AsyncLocalStorage();
const deferredValidation = new AsyncLocalStorage();

export function serializeWorkspaceMutation(input, task) {
export function serializeWorkspaceMutation(input, task, options = {}) {
const root = resolveWorkspaceRoot(input);
assertWorkspaceEngineVersion(root);
assertWorkspaceEngineVersion(root, options);
if (activeMutation.getStore() === root) return task(root);
const previous = mutationQueues.get(root) ?? Promise.resolve();
const run = previous.catch(() => {}).then(() => activeMutation.run(root, () => task(root)));
Expand Down
Loading
Loading