forgeos 0.1.0-alpha.50 → 0.1.0-alpha.51

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/AGENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- // @forge-generated generator=0.1.0-alpha.50 input=5f918b7dab43b47f2774c505897503c0b5ddc26d4022167410ed9f3b76190ee6 content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
1
+ // @forge-generated generator=0.1.0-alpha.51 input=56e2fea9ee90281ea32b5a5f6297bf827b851718f02b6c41f1b3f6f862a7eca8 content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
2
2
  # AGENTS.md
3
3
 
4
4
  <!-- forge-generated:start -->
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # forgeos
2
2
 
3
+ ## 0.1.0-alpha.51
4
+
5
+ ### Patch Changes
6
+
7
+ - Make the WorkOS FGA production doctor explicit enough for real P2 rollout.
8
+
9
+ - `forge workos fga doctor --real --json` now reports structured readiness
10
+ for the full hosted path: FGA plan, hosted seed evidence, hosted resource
11
+ type/resource sync, `organizationMembershipId` env, real proof state, and
12
+ production readiness.
13
+ - FGA JSON output now includes `membershipEnv`, `seedState`, and `readiness`
14
+ metadata so agents can choose the next command without scraping human text.
15
+ - Hosted FGA setup data now states that WorkOS resource type creation is not
16
+ automated by the WorkOS API/CLI path, while resource sync and authorization
17
+ checks remain ForgeOS-driven after those resource types exist.
18
+ - WorkOS real checks now read `deploy/.env.production` as an explicit deploy
19
+ evidence source, and `forge deploy check --production` runs WorkOS/FGA
20
+ doctors with those values in scope.
21
+ - Human output now distinguishes local FGA planning success from real
22
+ production-gate readiness.
23
+
3
24
  ## 0.1.0-alpha.50
4
25
 
5
26
  ### Patch Changes
package/docs/changelog.md CHANGED
@@ -6,6 +6,23 @@ The canonical source file in the repository is `CHANGELOG.md`.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.1.0-alpha.51
10
+
11
+ - `forge workos fga doctor --real --json` now reports a structured readiness
12
+ object for the full hosted FGA path: app-derived plan, hosted seed state,
13
+ real resource sync, membership env for WorkOS access checks, proof state, and
14
+ production readiness.
15
+ - FGA command JSON now includes `membershipEnv`, `seedState`, and `readiness`
16
+ metadata so agents can continue the no-dashboard WorkOS flow deterministically.
17
+ - Hosted FGA setup output now explicitly states that WorkOS resource type
18
+ creation is hosted WorkOS configuration, while ForgeOS can still sync
19
+ resources and run Authorization API checks after those types exist.
20
+ - WorkOS real checks now read `deploy/.env.production` as deploy evidence, and
21
+ `forge deploy check --production` runs WorkOS/FGA doctors with those values
22
+ in scope.
23
+ - Human FGA output now separates local planning success from real production
24
+ gate readiness.
25
+
9
26
  ## 0.1.0-alpha.50
10
27
 
11
28
  - WorkOS hosted setup now includes explicit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgeos",
3
- "version": "0.1.0-alpha.50",
3
+ "version": "0.1.0-alpha.51",
4
4
  "description": "Agent-native application framework and compiler for building Forge apps without a mandatory dashboard.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- {"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.50","releaseId":"forgeos@0.1.0-alpha.50+unknown","schemaVersion":"0.1.0"}
1
+ {"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.51","releaseId":"forgeos@0.1.0-alpha.51+unknown","schemaVersion":"0.1.0"}
@@ -1,4 +1,4 @@
1
- // @forge-generated generator=0.1.0-alpha.50 input=5f918b7dab43b47f2774c505897503c0b5ddc26d4022167410ed9f3b76190ee6 content=5bb2a5849e690e58325fbd495bd3b1ad63a42f77c8c1d06464b64c27f9192c6c
1
+ // @forge-generated generator=0.1.0-alpha.51 input=56e2fea9ee90281ea32b5a5f6297bf827b851718f02b6c41f1b3f6f862a7eca8 content=0e813cc871b8bd6a982fd4ccaf852bbc0a25e9dd49ab6a438de3bba9f0f840ab
2
2
  export const releaseManifest = {
3
3
  "defaultProvider": "local",
4
4
  "diagnostics": [],
@@ -19,7 +19,7 @@ export const releaseManifest = {
19
19
  "custom"
20
20
  ],
21
21
  "packageName": "forgeos",
22
- "packageVersion": "0.1.0-alpha.50",
23
- "releaseId": "forgeos@0.1.0-alpha.50+unknown",
22
+ "packageVersion": "0.1.0-alpha.51",
23
+ "releaseId": "forgeos@0.1.0-alpha.51+unknown",
24
24
  "schemaVersion": "0.1.0"
25
25
  } as const;
@@ -821,13 +821,15 @@ async function buildChecks(options: DeployCommandOptions): Promise<DeployCommand
821
821
  },
822
822
  });
823
823
  if (hasWorkOSIntegration(options.workspaceRoot)) {
824
- const workosDoctor = runWorkOSCommand({
825
- subcommand: "doctor",
826
- workspaceRoot: options.workspaceRoot,
827
- json: true,
828
- yes: false,
829
- dryRun: true,
830
- });
824
+ const workosDoctor = await withDeployEnv(options.workspaceRoot, async () =>
825
+ runWorkOSCommand({
826
+ subcommand: "doctor",
827
+ workspaceRoot: options.workspaceRoot,
828
+ json: true,
829
+ yes: false,
830
+ dryRun: true,
831
+ })
832
+ );
831
833
  checks.push({
832
834
  name: "workos-doctor",
833
835
  ok: workosDoctor.exitCode === 0,
@@ -853,15 +855,17 @@ async function buildChecks(options: DeployCommandOptions): Promise<DeployCommand
853
855
  command: "forge workos prove --real --file workos-seed.yml --json",
854
856
  details: seedState,
855
857
  });
856
- const workosFgaDoctor = runWorkOSCommand({
857
- subcommand: "fga",
858
- fgaAction: "doctor",
859
- workspaceRoot: options.workspaceRoot,
860
- json: true,
861
- yes: false,
862
- dryRun: true,
863
- real: options.production,
864
- });
858
+ const workosFgaDoctor = await withDeployEnv(options.workspaceRoot, async () =>
859
+ runWorkOSCommand({
860
+ subcommand: "fga",
861
+ fgaAction: "doctor",
862
+ workspaceRoot: options.workspaceRoot,
863
+ json: true,
864
+ yes: false,
865
+ dryRun: true,
866
+ real: options.production,
867
+ })
868
+ );
865
869
  checks.push({
866
870
  name: "workos-fga-proof",
867
871
  ok: !options.production || workosFgaDoctor.exitCode === 0,
@@ -324,6 +324,7 @@ function parseEnvText(text: string): Record<string, string> {
324
324
  function readRealEnv(workspaceRoot: string): Record<string, string> {
325
325
  return {
326
326
  ...parseEnvText(readRawText(workspaceRoot, ".env")),
327
+ ...parseEnvText(readRawText(workspaceRoot, "deploy/.env.production")),
327
328
  ...parseEnvText(readRawText(workspaceRoot, ".env.local")),
328
329
  ...Object.fromEntries(
329
330
  Object.entries(process.env)
@@ -677,12 +678,21 @@ export interface WorkOSFgaStateSummary {
677
678
  diagnostics: string[];
678
679
  }
679
680
 
681
+ export interface WorkOSFgaMembershipEnvSummary {
682
+ requiredEnv: string[];
683
+ presentEnv: string[];
684
+ missingEnv: string[];
685
+ jsonEnvPresent: boolean;
686
+ complete: boolean;
687
+ }
688
+
680
689
  export interface WorkOSFgaHostedSetup {
681
690
  requiredResourceTypes: string[];
682
691
  rootResourceType: "organization";
683
692
  missingResourceTypes: string[];
684
693
  requiredMembershipEnv: string[];
685
694
  managedBy: "hosted-workos";
695
+ resourceTypeAutomation: "not-supported-by-workos-api" | "not-needed";
686
696
  cliSupport: "resources-and-checks";
687
697
  sdkSupport: "resources-and-checks";
688
698
  docs: string[];
@@ -710,6 +720,19 @@ export interface WorkOSFgaSetupGuide {
710
720
  unsupportedAutomation: string[];
711
721
  }
712
722
 
723
+ export interface WorkOSFgaReadiness {
724
+ real: boolean;
725
+ planReady: boolean;
726
+ seedReady: boolean;
727
+ resourceTypesConfigured: boolean;
728
+ membershipEnvReady: boolean;
729
+ synced: boolean;
730
+ proved: boolean;
731
+ productionReady: boolean;
732
+ nextCommand: string;
733
+ nextActions: string[];
734
+ }
735
+
713
736
  function slugifyExternalIdPart(value: string): string {
714
737
  return value
715
738
  .trim()
@@ -951,6 +974,24 @@ function fgaMembershipEnvKey(organization: string): string {
951
974
  return `WORKOS_FGA_MEMBERSHIP_${suffix}`;
952
975
  }
953
976
 
977
+ function workOSFgaMembershipEnvSummary(
978
+ workspaceRoot: string,
979
+ organizations: string[],
980
+ ): WorkOSFgaMembershipEnvSummary {
981
+ const env = readRealEnv(workspaceRoot);
982
+ const requiredEnv = organizations.map(fgaMembershipEnvKey);
983
+ const jsonEnvPresent = hasValue(env, "WORKOS_FGA_MEMBERSHIPS_JSON") || hasValue(env, "WORKOS_FGA_TEST_MEMBERSHIPS");
984
+ const presentEnv = requiredEnv.filter((name) => hasValue(env, name));
985
+ const missingEnv = jsonEnvPresent ? [] : requiredEnv.filter((name) => !presentEnv.includes(name));
986
+ return {
987
+ requiredEnv,
988
+ presentEnv,
989
+ missingEnv,
990
+ jsonEnvPresent,
991
+ complete: jsonEnvPresent || missingEnv.length === 0,
992
+ };
993
+ }
994
+
954
995
  function extractMissingWorkOSFgaResourceTypes(data: unknown, manifest?: WorkOSFgaManifest): string[] {
955
996
  const missing = new Set<string>();
956
997
  const add = (value: unknown) => {
@@ -999,6 +1040,7 @@ function workOSFgaHostedSetup(manifest: WorkOSFgaManifest, sdkData?: unknown): W
999
1040
  missingResourceTypes,
1000
1041
  requiredMembershipEnv,
1001
1042
  managedBy: "hosted-workos",
1043
+ resourceTypeAutomation: requiredResourceTypes.length > 0 ? "not-supported-by-workos-api" : "not-needed",
1002
1044
  cliSupport: "resources-and-checks",
1003
1045
  sdkSupport: "resources-and-checks",
1004
1046
  docs: [
@@ -1179,11 +1221,14 @@ function writeWorkOSFgaSetupGuide(
1179
1221
 
1180
1222
  function fgaData(input: {
1181
1223
  action: WorkOSFgaAction;
1224
+ workspaceRoot: string;
1182
1225
  manifest: WorkOSFgaManifest;
1183
1226
  state: WorkOSFgaStateSummary;
1227
+ seedState?: WorkOSSeedStateSummary;
1184
1228
  real?: boolean;
1185
1229
  cliAuth?: WorkOSCliAuthSummary;
1186
1230
  workosSdk?: unknown;
1231
+ readiness?: WorkOSFgaReadiness;
1187
1232
  stateFile?: string;
1188
1233
  nextCommand?: string;
1189
1234
  nextActions?: string[];
@@ -1191,12 +1236,16 @@ function fgaData(input: {
1191
1236
  }): Record<string, unknown> {
1192
1237
  const hostedSetup = workOSFgaHostedSetup(input.manifest, input.workosSdk);
1193
1238
  const setupGuide = workOSFgaSetupGuide(input.manifest, hostedSetup);
1239
+ const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
1194
1240
  return {
1195
1241
  action: input.action,
1196
1242
  real: input.real ?? false,
1197
1243
  manifest: input.manifest,
1198
1244
  state: input.state,
1245
+ ...(input.seedState ? { seedState: input.seedState } : {}),
1246
+ ...(input.readiness ? { readiness: input.readiness } : {}),
1199
1247
  hostedSetup,
1248
+ membershipEnv,
1200
1249
  resourceTypeSetup: setupGuide.resourceTypes,
1201
1250
  setupGuide,
1202
1251
  ...(input.cliAuth ? { cliAuth: input.cliAuth } : {}),
@@ -2573,6 +2622,133 @@ function collectWorkOSFgaChecks(input: {
2573
2622
  ];
2574
2623
  }
2575
2624
 
2625
+ function workOSFgaReadiness(input: {
2626
+ workspaceRoot: string;
2627
+ file: string;
2628
+ manifest: WorkOSFgaManifest;
2629
+ state: WorkOSFgaStateSummary;
2630
+ seedState: WorkOSSeedStateSummary;
2631
+ real: boolean;
2632
+ }): WorkOSFgaReadiness {
2633
+ const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
2634
+ const planReady = input.manifest.diagnostics.length === 0 &&
2635
+ input.manifest.resourceTypes.includes("organization") &&
2636
+ input.manifest.proofScenarios.some((scenario) => scenario.expected === "allow") &&
2637
+ input.manifest.proofScenarios.some((scenario) => scenario.expected === "deny");
2638
+ const seedReady = !input.real || Boolean(input.seedState.exists && input.seedState.valid && input.seedState.matchesSeedHash === true);
2639
+ const synced = Boolean(input.state.exists && input.state.valid && input.state.matchesManifestHash === true);
2640
+ const resourceTypesConfigured = !input.real || Boolean(synced && input.state.mode === "real" && input.state.sdkOk === true);
2641
+ const membershipEnvReady = !input.real || membershipEnv.complete;
2642
+ const proved = !input.real
2643
+ ? Boolean(input.state.provedAt)
2644
+ : Boolean(synced && input.state.mode === "real" && input.state.sdkOk === true && input.state.provedAt);
2645
+ const productionReady = Boolean(planReady && seedReady && resourceTypesConfigured && membershipEnvReady && proved);
2646
+ let nextCommand = `forge workos fga plan --file ${input.file} --write --json`;
2647
+ if (planReady && input.real && !seedReady) {
2648
+ nextCommand = `forge workos prove --real --file ${input.file} --json`;
2649
+ } else if (planReady && input.real && !resourceTypesConfigured) {
2650
+ nextCommand = `forge workos fga sync --real --file ${input.file} --write --json`;
2651
+ } else if (planReady && input.real && !membershipEnvReady) {
2652
+ nextCommand = `forge workos fga prove --real --file ${input.file} --json`;
2653
+ } else if (planReady && input.real && !proved) {
2654
+ nextCommand = `forge workos fga prove --real --file ${input.file} --json`;
2655
+ } else if (planReady && !input.real && !synced) {
2656
+ nextCommand = `forge workos fga sync --file ${input.file} --json`;
2657
+ } else if (planReady && !input.real && !proved) {
2658
+ nextCommand = `forge workos fga prove --file ${input.file} --json`;
2659
+ } else if (productionReady || (planReady && !input.real)) {
2660
+ nextCommand = "forge deploy check --production --json";
2661
+ }
2662
+ const nextActions = [
2663
+ ...(planReady ? [] : [`repair FGA manifest gaps, then run forge workos fga plan --file ${input.file} --write --json`]),
2664
+ ...(input.real && !seedReady ? [`apply/prove hosted WorkOS seed: forge workos prove --real --file ${input.file} --json`] : []),
2665
+ ...(input.real && !resourceTypesConfigured
2666
+ ? [
2667
+ "configure hosted WorkOS FGA resource types listed in resourceTypeSetup",
2668
+ `sync real WorkOS FGA resources: forge workos fga sync --real --file ${input.file} --write --json`,
2669
+ ]
2670
+ : []),
2671
+ ...(input.real && !membershipEnvReady
2672
+ ? [`set WORKOS_FGA_MEMBERSHIPS_JSON or ${membershipEnv.missingEnv.join(", ") || "WORKOS_FGA_MEMBERSHIP_<ORG>"} before real access checks`]
2673
+ : []),
2674
+ ...(input.real && membershipEnvReady && resourceTypesConfigured && !proved
2675
+ ? [`prove real WorkOS FGA access checks: forge workos fga prove --real --file ${input.file} --json`]
2676
+ : []),
2677
+ ...(productionReady ? ["rerun forge deploy check --production --json"] : []),
2678
+ ...(!input.real && planReady && !synced ? [`run forge workos fga sync --file ${input.file} --json`] : []),
2679
+ ...(!input.real && planReady && synced && !proved ? [`run forge workos fga prove --file ${input.file} --json`] : []),
2680
+ ];
2681
+ return {
2682
+ real: input.real,
2683
+ planReady,
2684
+ seedReady,
2685
+ resourceTypesConfigured,
2686
+ membershipEnvReady,
2687
+ synced,
2688
+ proved,
2689
+ productionReady,
2690
+ nextCommand,
2691
+ nextActions,
2692
+ };
2693
+ }
2694
+
2695
+ function collectWorkOSFgaDoctorChecks(input: {
2696
+ workspaceRoot: string;
2697
+ file: string;
2698
+ manifest: WorkOSFgaManifest;
2699
+ state: WorkOSFgaStateSummary;
2700
+ seedState: WorkOSSeedStateSummary;
2701
+ real: boolean;
2702
+ }): WorkOSCheck[] {
2703
+ const readiness = workOSFgaReadiness(input);
2704
+ const membershipEnv = workOSFgaMembershipEnvSummary(input.workspaceRoot, input.manifest.organizations);
2705
+ return [
2706
+ ...collectWorkOSFgaChecks({
2707
+ manifest: input.manifest,
2708
+ state: input.state,
2709
+ requireState: input.real,
2710
+ requireRealState: input.real,
2711
+ requireProof: input.real,
2712
+ }),
2713
+ {
2714
+ name: "fga-seed-state",
2715
+ ok: !input.real || readiness.seedReady,
2716
+ detail: !input.real
2717
+ ? "hosted seed evidence is only required for --real doctor"
2718
+ : readiness.seedReady
2719
+ ? `${WORKOS_SEED_STATE_FILE} matches ${input.file}`
2720
+ : `real FGA proof requires hosted seed evidence matching ${input.file}; run forge workos prove --real --file ${input.file} --json`,
2721
+ },
2722
+ {
2723
+ name: "fga-hosted-resource-types",
2724
+ ok: !input.real || readiness.resourceTypesConfigured,
2725
+ detail: !input.real
2726
+ ? "hosted WorkOS resource type existence is only verified by --real sync/prove"
2727
+ : readiness.resourceTypesConfigured
2728
+ ? `${WORKOS_FGA_STATE_FILE} records successful real Authorization API resource sync`
2729
+ : "real FGA requires hosted WorkOS resource types for every non-organization resource; run forge workos fga plan --write and configure any listed resource types before sync",
2730
+ },
2731
+ {
2732
+ name: "fga-membership-env",
2733
+ ok: !input.real || readiness.membershipEnvReady,
2734
+ detail: !input.real
2735
+ ? "organizationMembershipId env is only required for real WorkOS access checks"
2736
+ : readiness.membershipEnvReady
2737
+ ? `membership env is present${membershipEnv.jsonEnvPresent ? " through WORKOS_FGA_MEMBERSHIPS_JSON" : ` through ${membershipEnv.presentEnv.join(", ")}`}`
2738
+ : `missing organizationMembershipId env for real checks: WORKOS_FGA_MEMBERSHIPS_JSON or ${membershipEnv.missingEnv.join(", ")}`,
2739
+ },
2740
+ {
2741
+ name: "fga-production-readiness",
2742
+ ok: !input.real || readiness.productionReady,
2743
+ detail: !input.real
2744
+ ? "run forge workos fga doctor --real --json for production FGA gates"
2745
+ : readiness.productionReady
2746
+ ? "real WorkOS FGA seed, resource sync, membership env, and proof are current"
2747
+ : `real WorkOS FGA is not production-ready; next command: ${readiness.nextCommand}`,
2748
+ },
2749
+ ];
2750
+ }
2751
+
2576
2752
  export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSCommandResult {
2577
2753
  const action = options.fgaAction ?? "doctor";
2578
2754
  const file = options.file ?? DEFAULT_SEED_FILE;
@@ -2608,8 +2784,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
2608
2784
  applied: false,
2609
2785
  data: fgaData({
2610
2786
  action,
2787
+ workspaceRoot: options.workspaceRoot,
2611
2788
  manifest,
2612
2789
  state,
2790
+ seedState,
2613
2791
  real,
2614
2792
  ...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
2615
2793
  nextCommand: `forge workos fga sync --file ${file} --json`,
@@ -2642,8 +2820,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
2642
2820
  applied: false,
2643
2821
  data: fgaData({
2644
2822
  action,
2823
+ workspaceRoot: options.workspaceRoot,
2645
2824
  manifest,
2646
2825
  state,
2826
+ seedState,
2647
2827
  real,
2648
2828
  ...(cliAuth ? { cliAuth } : {}),
2649
2829
  ...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
@@ -2661,8 +2841,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
2661
2841
  applied: false,
2662
2842
  data: fgaData({
2663
2843
  action,
2844
+ workspaceRoot: options.workspaceRoot,
2664
2845
  manifest,
2665
2846
  state,
2847
+ seedState,
2666
2848
  real,
2667
2849
  ...(cliAuth ? { cliAuth } : {}),
2668
2850
  ...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
@@ -2688,8 +2870,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
2688
2870
  applied: false,
2689
2871
  data: fgaData({
2690
2872
  action,
2873
+ workspaceRoot: options.workspaceRoot,
2691
2874
  manifest,
2692
2875
  state,
2876
+ seedState,
2693
2877
  real,
2694
2878
  ...(cliAuth ? { cliAuth } : {}),
2695
2879
  workosSdk: sdk.data,
@@ -2718,8 +2902,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
2718
2902
  applied: true,
2719
2903
  data: fgaData({
2720
2904
  action,
2905
+ workspaceRoot: options.workspaceRoot,
2721
2906
  manifest,
2722
2907
  state,
2908
+ seedState,
2723
2909
  real,
2724
2910
  ...(cliAuth ? { cliAuth } : {}),
2725
2911
  ...(sdk ? { workosSdk: sdk.data } : {}),
@@ -2789,8 +2975,10 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
2789
2975
  applied: Boolean(ok && !options.dryRun),
2790
2976
  data: fgaData({
2791
2977
  action,
2978
+ workspaceRoot: options.workspaceRoot,
2792
2979
  manifest,
2793
2980
  state,
2981
+ seedState,
2794
2982
  real,
2795
2983
  ...(cliAuth ? { cliAuth } : {}),
2796
2984
  ...(sdk ? { workosSdk: sdk.data } : {}),
@@ -2804,20 +2992,40 @@ export function runWorkOSFgaCommand(options: WorkOSCommandOptions): WorkOSComman
2804
2992
  };
2805
2993
  }
2806
2994
 
2807
- const ok = checks.every((check) => check.ok);
2995
+ const doctorChecks = collectWorkOSFgaDoctorChecks({
2996
+ workspaceRoot: options.workspaceRoot,
2997
+ file,
2998
+ manifest,
2999
+ state,
3000
+ seedState,
3001
+ real,
3002
+ });
3003
+ const readiness = workOSFgaReadiness({
3004
+ workspaceRoot: options.workspaceRoot,
3005
+ file,
3006
+ manifest,
3007
+ state,
3008
+ seedState,
3009
+ real,
3010
+ });
3011
+ const ok = doctorChecks.every((check) => check.ok);
2808
3012
  return {
2809
3013
  ok,
2810
3014
  kind: "workos-fga",
2811
- checks,
3015
+ checks: doctorChecks,
2812
3016
  command,
2813
3017
  applied: false,
2814
3018
  data: fgaData({
2815
3019
  action,
3020
+ workspaceRoot: options.workspaceRoot,
2816
3021
  manifest,
2817
3022
  state,
3023
+ seedState,
3024
+ readiness,
2818
3025
  real,
2819
3026
  ...(writtenSetupGuidePath ? { setupGuidePath: writtenSetupGuidePath } : {}),
2820
- nextCommand: state.exists ? `forge workos fga prove --file ${file} --json` : `forge workos fga sync --file ${file} --json`,
3027
+ nextCommand: readiness.nextCommand,
3028
+ nextActions: readiness.nextActions,
2821
3029
  }),
2822
3030
  exitCode: ok ? 0 : 1,
2823
3031
  };
@@ -2953,6 +3161,8 @@ export function formatWorkOSHuman(result: WorkOSCommandResult): string {
2953
3161
  lines.push(`WorkOS FGA sync recorded${data.stateFile ? ` in ${data.stateFile}` : ""}; run ${data.nextCommand ?? "forge workos fga prove --json"}.`);
2954
3162
  } else if (result.ok && data.action === "prove") {
2955
3163
  lines.push(`WorkOS FGA proof passed${data.real ? " for real-mode state" : " locally"}; production deploy gates can inspect the FGA state.`);
3164
+ } else if (result.ok && data.action === "doctor") {
3165
+ lines.push(`WorkOS FGA doctor passed${data.real ? " for real production gates" : " for local planning"}; run ${data.nextCommand ?? "forge workos fga sync --json"} if you need the next proof step.`);
2956
3166
  } else if (!result.ok) {
2957
3167
  lines.push("WorkOS FGA check failed; inspect manifest diagnostics, seed coverage, and FGA state.");
2958
3168
  for (const action of data.nextActions ?? []) {
@@ -1,3 +1,3 @@
1
- export const FORGEOS_VERSION = "0.1.0-alpha.50";
1
+ export const FORGEOS_VERSION = "0.1.0-alpha.51";
2
2
  export const GENERATOR_VERSION = FORGEOS_VERSION;
3
3
  export const CLI_VERSION = FORGEOS_VERSION;