@sanity/ailf-studio 2.2.1 → 2.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/dist/index.d.ts CHANGED
@@ -119,10 +119,11 @@ declare const RunTaskEvaluationAction: DocumentActionComponent;
119
119
  * ```
120
120
  *
121
121
  * `ailfStructure` renders filtered entries for `ailf.task`, `ailf.team`,
122
- * `ailf.featureArea`, and `ailf.report` in place of their default Studio
123
- * list items, plus a top-level diagnostic for teams whose channel events
124
- * reference unknown event-type strings. Other document types are preserved
125
- * at their Studio default via `S.documentTypeListItems().filter(...)`.
122
+ * `ailf.user`, `ailf.featureArea`, and `ailf.report` in place of their
123
+ * default Studio list items, plus a top-level diagnostic for teams whose
124
+ * channel events reference unknown event-type strings. Other document types
125
+ * are preserved at their Studio default via
126
+ * `S.documentTypeListItems().filter(...)`.
126
127
  * Consumers who already maintain a custom structure can splice individual
127
128
  * helpers in via the per-type exports.
128
129
  */
@@ -135,9 +136,10 @@ declare const RunTaskEvaluationAction: DocumentActionComponent;
135
136
  declare function ailfTaskStructureItem(S: StructureBuilder): ReturnType<StructureBuilder["listItem"]>;
136
137
  /**
137
138
  * Full structure resolver that replaces the default entries for
138
- * `ailf.task`, `ailf.team`, `ailf.featureArea`, and `ailf.report` with
139
- * filtered AILF views, exposes a top-level unknown-events diagnostic,
140
- * and keeps every other document type list item at its Studio default.
139
+ * `ailf.task`, `ailf.team`, `ailf.user`, `ailf.featureArea`, and
140
+ * `ailf.report` with filtered AILF views, exposes a top-level
141
+ * unknown-events diagnostic, and keeps every other document type list item
142
+ * at its Studio default.
141
143
  */
142
144
  declare const ailfStructure: StructureResolver;
143
145
 
@@ -512,6 +514,7 @@ declare const featureAreaSchema: {
512
514
  preview?: sanity.PreviewConfig<{
513
515
  areaId: string;
514
516
  description: string;
517
+ team: string;
515
518
  }, Record<string, unknown>> | undefined;
516
519
  };
517
520
 
@@ -639,6 +642,39 @@ declare const teamSchema: {
639
642
  }, Record<string, unknown>> | undefined;
640
643
  };
641
644
 
645
+ /**
646
+ * schema/user.ts
647
+ *
648
+ * Sanity document schema for `ailf.user` — a per-account user document that
649
+ * stores self-declared team affiliation (references to `ailf.team`) plus UI
650
+ * preferences, and is the primary source for dashboard personalization.
651
+ *
652
+ * This schema is a deliberate **subset** of the canonical `AilfUser` domain
653
+ * type (`packages/core/src/types/user.ts`), per D0045 (TS-first domain type →
654
+ * Zod `satisfies` → Sanity-as-subset; Studio schemas are hand-authored and
655
+ * never TypeGen-inverted).
656
+ *
657
+ * Every field is `readOnly: true`. The dashboard App SDK is the write surface
658
+ * (lazy `createIfNotExists` + patch via `useApplyDocumentActions`); Studio is
659
+ * for operator inspection/triage only. `readOnly` is a Studio-UI concern and
660
+ * does not affect the dashboard's programmatic writes.
661
+ *
662
+ * Relationship to D0055: `ailf.user` is the user-side identity/affiliation axis
663
+ * deferred by the team-entity work — distinct from `area.team` ownership and
664
+ * `team.notifications[].scope` subscription.
665
+ *
666
+ * @see docs/design-docs/user-settings.md § Type-architecture placement
667
+ */
668
+ declare const userSchema: {
669
+ type: "document";
670
+ name: "ailf.user";
671
+ } & Omit<sanity.DocumentDefinition, "preview"> & {
672
+ preview?: sanity.PreviewConfig<{
673
+ displayName: string;
674
+ email: string;
675
+ }, Record<string, unknown>> | undefined;
676
+ };
677
+
642
678
  /**
643
679
  * schema/webhook-config.ts
644
680
  *
@@ -1230,4 +1266,4 @@ interface AilfPluginOptions {
1230
1266
  */
1231
1267
  declare const ailfPlugin: sanity.Plugin<void | AilfPluginOptions>;
1232
1268
 
1233
- export { type AilfPluginOptions, ArchiveTaskAction, AssertionInput, CanonicalDocInput, type ComparisonData, type ContentImpactItem, GraduateToNativeAction, HelpDrawer, HelpProvider, MirrorBanner, type PerModelData, type ProvenanceData, ReleasePicker, type ReportDetail, type ReportListItem, RestoreTaskAction, type RunEvaluationActionOptions, RunTaskEvaluationAction, type ScoreItem, type SummaryData, SyncStatusBadge, type TimelineDataPoint, ailfPlugin, ailfStructure, ailfTaskStructureItem, ailfTool, articleSearchQuery, comparisonPairQuery, contentImpactQuery, createRunEvaluationAction, deriveHelpTopic, distinctAreasQuery, distinctModesQuery, distinctPerspectivesQuery, distinctSourcesQuery, distinctTargetDocumentsQuery, distinctTriggersQuery, evalRequestSchema, featureAreaSchema, findTopic, latestReportsQuery, recentDocumentEvalsQuery, referenceSolutionSchema, reportDetailQuery, reportSchema, scoreTimelineQuery, searchTopics, taskSchema, teamSchema, useHelp, webhookConfigSchema };
1269
+ export { type AilfPluginOptions, ArchiveTaskAction, AssertionInput, CanonicalDocInput, type ComparisonData, type ContentImpactItem, GraduateToNativeAction, HelpDrawer, HelpProvider, MirrorBanner, type PerModelData, type ProvenanceData, ReleasePicker, type ReportDetail, type ReportListItem, RestoreTaskAction, type RunEvaluationActionOptions, RunTaskEvaluationAction, type ScoreItem, type SummaryData, SyncStatusBadge, type TimelineDataPoint, ailfPlugin, ailfStructure, ailfTaskStructureItem, ailfTool, articleSearchQuery, comparisonPairQuery, contentImpactQuery, createRunEvaluationAction, deriveHelpTopic, distinctAreasQuery, distinctModesQuery, distinctPerspectivesQuery, distinctSourcesQuery, distinctTargetDocumentsQuery, distinctTriggersQuery, evalRequestSchema, featureAreaSchema, findTopic, latestReportsQuery, recentDocumentEvalsQuery, referenceSolutionSchema, reportDetailQuery, reportSchema, scoreTimelineQuery, searchTopics, taskSchema, teamSchema, useHelp, userSchema, webhookConfigSchema };
package/dist/index.js CHANGED
@@ -609,6 +609,17 @@ following:
609
609
  "scoring-model"
610
610
  ]
611
611
  },
612
+ {
613
+ "id": "failure-modes",
614
+ "title": "Failure Modes",
615
+ "body": "## What this view is for\n\nThe Recommendations view tells you which fixes to make. This view tells you what\nkind of problem you have. It groups the run's weaknesses by the documentation\nissue behind them, so you can see patterns across the whole evaluation rather\nthan one fix at a time. If most of your weak spots are the same kind of problem,\nthat is a signal about how to spend your docs effort.\n\n## What you are looking at\n\nRecent reports show **interpretive cards** drawn from the run's diagnosis:\n\n- **Weakest area** names the single feature area dragging the score down most,\n the failure mode behind it, and a confidence level with the sample size, so\n you know how strong the signal is.\n- **Failure mode** highlights one category of problem, which scoring dimension\n it shows up in, and how often it occurred across the tests that were checked.\n- **Area summary** gives a plain-language read on how an area is doing and why.\n\nOlder reports show a **category breakdown** instead. Each failure category is a\nchip with a count. Selecting a chip lists the gaps in that category, and each\ngap shows an estimated score lift if fixed, a confidence level, a short\nremediation note, and the specific tasks that exposed it. You can click a task\nto jump to it.\n\n## The failure modes\n\nEach weakness is sorted into one of these categories. The category is the\nfastest way to know what kind of work the fix needs:\n\n- **Missing docs**: the doc the model needed does not exist or is not indexed.\n The fix is to write new documentation.\n- **Incorrect docs**: a doc has a factual error or a wrong example. The fix is\n to correct it.\n- **Outdated docs**: a doc exists but reflects a previous API surface. The fix\n is to bring it up to date.\n- **Poor structure**: the information is correct but hard for an agent to find\n or skim. The fix is to reorganize or clarify.\n- **Model limitation**: the model struggles even with correct docs available.\n This is not a documentation problem, so treat it as context rather than a\n to-do.\n- **Unclassified**: the run could not categorize the weakness. Use the linked\n tasks and the grader's notes to judge it yourself.\n\nDepending on the evaluation mode you may see additional categories, including\nones specific to agent behavior such as tool misuse or missing error handling.\n\n## How to use it\n\nStart with the category that has the most gaps or the highest combined lift. The\ncategory tells you the shape of the work before you open a single page: write,\ncorrect, update, or restructure. Categories that are not documentation problems,\nsuch as model limitation, are worth noting but are not yours to fix in the docs.\n\n## Related views\n\n- **Recommendations** turns these weaknesses into a ranked list of specific\n edits.\n- **Low-scoring judgments** shows the grader's raw notes on the tests that\n scored lowest, which is the most granular signal behind any failure mode.\n\n## When this view is empty\n\nIf a report shows no failure modes, the evaluation either classified nothing\nworth flagging or the run predates this view. A clean result here usually means\nthe docs held up across the evaluated tasks.",
616
+ "source": "docs/help/failure-modes.md",
617
+ "related": [
618
+ "recommendations",
619
+ "scoring-model",
620
+ "negative-doc-lift"
621
+ ]
622
+ },
612
623
  {
613
624
  "id": "getting-started",
614
625
  "title": "Getting Started",
@@ -661,11 +672,12 @@ Click into any report for the full breakdown: per-area scores, diagnostics, and
661
672
  {
662
673
  "id": "interpreting-diagnostics",
663
674
  "title": "Interpreting Diagnostics",
664
- "body": "## The diagnostics tab\n\nWhen you open a report and click the **Diagnostics** tab, you see a health\nsummary of your documentation across all feature areas. This is the most\nactionable view in the dashboard \u2014 it tells you exactly where to focus your doc\nimprovement efforts.\n\n## Health categories\n\nFeature areas are grouped into three health bands:\n\n- **Strong (80+)** \u2014 Docs are working well. AI agents produce correct, complete\n implementations. No action needed unless you see regression.\n- **Needs Attention (70\u201379)** \u2014 Docs are okay but have gaps. There may be\n specific dimensions (like code correctness or doc coverage) dragging the score\n down. Worth investigating.\n- **Weak (below 70)** \u2014 Docs are not providing enough support. AI agents\n consistently struggle with these features. These need priority attention.\n\n## Strengths vs. Issues\n\nThe diagnostics tab has two sub-views:\n\n**Strengths** highlights what's working: high-scoring areas, strong dimensions,\nand areas where agents successfully find and use your docs. Use this to\nunderstand what good looks like in your docs \u2014 and replicate it elsewhere.\n\n**Issues** lists the problems: weak areas, dimensions scoring below 50, negative\ndoc lift, retrieval problems, and (if gap analysis was run) specific\nrecommendations with estimated score lift.\n\n## Key diagnostic signals\n\n| Signal | What it means | What to do |\n| ------------------------------ | ------------------------------------------ | ---------------------------------------- |\n| **Negative doc lift** | Docs are worse than no docs | Rewrite or remove the offending docs |\n| **Large retrieval gap** | Good docs exist but agents can't find them | Improve page titles, metadata, SEO |\n| **Low code correctness** | Agents find the docs but produce bad code | Add or fix code examples |\n| **Low doc coverage** | The docs don't cover what the task needs | Write new documentation |\n| **Efficiency anomaly (>100%)** | Agents do better without gold docs | Injected docs may be confusing the model |",
675
+ "body": "## Reading the health of your docs\n\nA report scores each feature area on how well your documentation lets AI coding\ntools implement that feature. Reading those scores well is what turns a number\ninto a plan: it tells you where the docs are working, where they are not, and\nwhat kind of problem you are dealing with.\n\n## Health bands\n\nEach area's score falls into one of three bands:\n\n- **Strong (80 and above)**: docs are working well. Agents produce correct,\n complete implementations. No action needed unless you see a regression.\n- **Needs attention (70 to 79)**: docs are okay but have gaps. A specific\n dimension such as code correctness or doc coverage may be dragging the score\n down. Worth investigating.\n- **Weak (below 70)**: docs are not providing enough support. Agents\n consistently struggle with these features. These need priority attention.\n\n## Strong areas are signal too\n\nIt is easy to focus only on what is broken, but the strong areas are worth\nreading. They show what good looks like in your docs: clear structure, accurate\nexamples, the patterns agents can follow. When you fix a weak area, that is the\nbar to copy.\n\n## Key diagnostic signals\n\nA low score has a reason behind it. These signals tell you which reason, and\nwhat to do about it:\n\n| Signal | What it means | What to do |\n| ------------------------------ | ------------------------------------------- | ---------------------------------------- |\n| **Negative doc lift** | Docs are worse than no docs | Rewrite or remove the offending docs |\n| **Large retrieval gap** | Good docs exist but agents cannot find them | Improve page titles, metadata, structure |\n| **Low code correctness** | Agents find the docs but produce bad code | Add or fix code examples |\n| **Low doc coverage** | The docs do not cover what the task needs | Write new documentation |\n| **Efficiency anomaly (>100%)** | Agents do better without the docs | Injected docs may be confusing the model |\n\n## Where to go next\n\nWhen you know which areas are weak and why, the **Recommendations** view turns\nthat into a ranked list of specific edits, and the **Failure modes** view groups\nthe weaknesses by the kind of documentation problem behind them.",
665
676
  "source": "docs/help/interpreting-diagnostics.md",
666
677
  "related": [
667
- "scoring-model",
668
- "weaknesses-recommendations"
678
+ "recommendations",
679
+ "failure-modes",
680
+ "scoring-model"
669
681
  ]
670
682
  },
671
683
  {
@@ -678,6 +690,88 @@ Click into any report for the full breakdown: per-area scores, diagnostics, and
678
690
  "comparing-runs"
679
691
  ]
680
692
  },
693
+ {
694
+ "id": "recommendations",
695
+ "title": "Recommendations",
696
+ "body": `## What this view is for
697
+
698
+ This is the "what do I fix" view. The scores tell you how well your
699
+ documentation supports AI coding tools. This view turns those scores into a
700
+ ranked list of specific changes, so you can spend your time on the edits that
701
+ should move the score the most.
702
+
703
+ Everything here comes from the same evaluation run you are looking at, and it
704
+ points at your own documentation pages rather than giving generic advice.
705
+
706
+ ## What you are looking at
707
+
708
+ Recent reports show a set of **diagnosis cards**. Each card answers one question
709
+ about the run.
710
+
711
+ **Top recommendations** is the main card. It opens with a short summary, then
712
+ lists a few suggested changes ranked by priority. Each suggestion has:
713
+
714
+ - A **priority** tag of high, medium, or low that tells you what to do first.
715
+ - A **title** that names the change in one line.
716
+ - A **description** of the specific fix, usually quoting the exact symbol,
717
+ query, or pattern involved.
718
+ - A **doc reference** showing which page, and the section when it is known, the
719
+ change applies to. Every reference points to a real page that was part of this
720
+ run, so you can open it and start editing.
721
+
722
+ You may also see supporting cards:
723
+
724
+ - **Doc attribution spotlight** shows which documentation pages most influenced
725
+ the results, and whether each one helped or hurt. Use it to confirm a
726
+ recommendation is pointing at the right page.
727
+ - **Low-confidence attribution** lists results where the link between a doc and
728
+ an outcome was uncertain. Treat anything flagged here as a lead to verify, not
729
+ a settled conclusion.
730
+ - **Regression vs baseline** appears when you are comparing against an earlier
731
+ run. It shows which areas moved up or down and the likely reason for each
732
+ change.
733
+
734
+ ## How to use it
735
+
736
+ Work top down. Start with the high-priority suggestions, open the referenced
737
+ page, and make the change. Priority reflects how much each change is expected to
738
+ help, so the top of the list is usually where your effort goes furthest.
739
+
740
+ The recommendations are written by a model that reads this run's results. They
741
+ are grounded in your actual docs and cannot reference a page that was not in the
742
+ run, but they are still suggestions. Read the linked page before acting, and use
743
+ the confidence signals to decide how much to trust each item.
744
+
745
+ ## Where this comes from
746
+
747
+ A recommendation is the end of a chain: a test scored low, the grader said why,
748
+ the run classified that into a failure mode, and this view proposes the edit. If
749
+ you want to see the failure modes themselves, grouped by category, open the
750
+ **Failure modes** view. If you want the grader's raw notes on the lowest scores,
751
+ open the **Low-scoring judgments** view.
752
+
753
+ ## Older reports
754
+
755
+ Reports created before the diagnosis cards shipped show a simpler list instead.
756
+ Each row names a feature area, the failure mode behind it, an estimated score
757
+ lift if you fix it, a confidence level, and the tasks that exposed the gap. The
758
+ estimated lift is conservative. It assumes fixing the gap raises the weak
759
+ dimension only to the median of the others, so the real improvement can be
760
+ higher.
761
+
762
+ ## When this view is empty
763
+
764
+ If a report shows no recommendations, the evaluation either ran and found
765
+ nothing worth flagging, or the run predates this feature. A score with no
766
+ recommendations is usually a good sign, because it means the docs held up across
767
+ the evaluated tasks.`,
768
+ "source": "docs/help/recommendations.md",
769
+ "related": [
770
+ "failure-modes",
771
+ "interpreting-diagnostics",
772
+ "scoring-model"
773
+ ]
774
+ },
681
775
  {
682
776
  "id": "retrieval-gap",
683
777
  "title": "Retrieval Gap & Infrastructure Efficiency",
@@ -700,17 +794,6 @@ Click into any report for the full breakdown: per-area scores, diagnostics, and
700
794
  "eval-modes"
701
795
  ]
702
796
  },
703
- {
704
- "id": "weaknesses-recommendations",
705
- "title": "Weaknesses & Recommendations",
706
- "body": "## Understanding weaknesses\n\nThe Issues sub-tab in Diagnostics lists every area or dimension that scored\nbelow threshold. Each weakness entry shows:\n\n- **The feature area** \u2014 Which product feature is affected (e.g., GROQ,\n Functions, Webhooks).\n- **The bottleneck dimension** \u2014 Which scoring dimension is dragging the area\n down: task completion, code correctness, or doc coverage.\n- **The score** \u2014 How far below threshold the dimension scored.\n\n## Gap analysis recommendations\n\nWhen an evaluation runs with gap analysis enabled, the dashboard shows\n**prioritized recommendations** \u2014 specific actions ranked by estimated impact.\n\nEach recommendation includes:\n\n- **Failure mode** \u2014 The type of doc problem identified:\n - `missing-docs` \u2014 The functionality isn't documented at all.\n - `incorrect-docs` \u2014 The docs contain factual errors.\n - `outdated-docs` \u2014 The docs describe an old API version or pattern.\n - `poor-structure` \u2014 The docs exist but are hard to find or understand.\n- **Estimated lift** \u2014 How many score points fixing this gap would add. Based on\n raising the bottleneck dimension to the median of non-bottleneck dimensions.\n Conservative estimate \u2014 actual improvement may be higher.\n- **Confidence** \u2014 How sure the analysis is about this diagnosis (high, medium,\n or low).\n- **Affected tasks** \u2014 Which specific evaluation tasks exposed this gap.\n\n## Diagnosis cards\n\nEvery published report now carries a **diagnosis artifact** \u2014 a set of cards\nproduced by the post-pipeline hook (`ailf interpret`). The Studio diagnosis\npanel renders these cards directly; the dashboard's Recommendations and\nFailure-modes panels migrate to the same source in a follow-up.\n\nThe hook runs by default for every pipeline invocation. To opt out for a single\nrun, pass `--no-summary`; to opt out in CI, set `AILF_INTERPRET_ON_RUN=0` in the\nworkflow env block; to opt out project-wide, set `summary.onRun: never` in\n`.ailf/config.yaml`.\n\n## Low-scoring judgments\n\nBelow the recommendations, you'll find the **grader's explanations** for tests\nthat scored below 70. These are the raw assessments from the grading model\nexplaining exactly what went wrong \u2014 missing API calls, incorrect patterns,\nhallucinated features, etc.\n\nEach judgment shows the task, the dimension, the score, and the grader's natural\nlanguage reason. These are the most granular diagnostic signal available and\noften point directly to the doc section that needs fixing.",
707
- "source": "docs/help/weaknesses-recommendations.md",
708
- "related": [
709
- "interpreting-diagnostics",
710
- "scoring-model",
711
- "negative-doc-lift"
712
- ]
713
- },
714
797
  {
715
798
  "id": "how-agents-work",
716
799
  "title": "How AI Agents Find Documentation",
@@ -1296,16 +1379,22 @@ var featureAreaSchema = defineType2({
1296
1379
  ],
1297
1380
  name: "ailf.featureArea",
1298
1381
  preview: {
1299
- prepare({ areaId, description }) {
1382
+ // `team` derefs the owning-team reference one hop to its displayName,
1383
+ // so the area list shows ownership at a glance (the dataset-level
1384
+ // "Unowned areas" diagnostic lives in structure.ts).
1385
+ prepare({ areaId, description, team }) {
1300
1386
  const id = areaId !== null && typeof areaId === "object" && "current" in areaId ? areaId.current : void 0;
1387
+ const idStr = typeof id === "string" ? id : "";
1388
+ const teamStr = typeof team === "string" ? team : "";
1301
1389
  return {
1302
- subtitle: typeof id === "string" ? id : "",
1390
+ subtitle: teamStr ? `${idStr} \xB7 ${teamStr}` : idStr,
1303
1391
  title: typeof description === "string" ? description : "Feature Area"
1304
1392
  };
1305
1393
  },
1306
1394
  select: {
1307
1395
  areaId: "areaId",
1308
- description: "description"
1396
+ description: "description",
1397
+ team: "team.displayName"
1309
1398
  }
1310
1399
  },
1311
1400
  title: "AILF Feature Area",
@@ -4409,16 +4498,97 @@ var teamSchema = defineType6({
4409
4498
  type: "document"
4410
4499
  });
4411
4500
 
4501
+ // src/schema/user.ts
4502
+ import { defineArrayMember as defineArrayMember2, defineField as defineField7, defineType as defineType7 } from "sanity";
4503
+ var userSchema = defineType7({
4504
+ fields: [
4505
+ defineField7({
4506
+ description: "Sanity account id (CurrentUser.id) \u2014 the stable, globally-unique key. Mirrored as a field so GROQ can join/filter; the document _id is the deterministic `ailf.user.${sanityUserId}`.",
4507
+ name: "sanityUserId",
4508
+ readOnly: true,
4509
+ title: "Sanity User ID",
4510
+ type: "string"
4511
+ }),
4512
+ defineField7({
4513
+ description: "Denormalized email for display / joins (lowercased on write).",
4514
+ name: "email",
4515
+ readOnly: true,
4516
+ title: "Email",
4517
+ type: "string"
4518
+ }),
4519
+ defineField7({
4520
+ description: "Display-name snapshot from CurrentUser.name.",
4521
+ name: "displayName",
4522
+ readOnly: true,
4523
+ title: "Display Name",
4524
+ type: "string"
4525
+ }),
4526
+ defineField7({
4527
+ description: "Self-declared team affiliation \u2014 drives dashboard personalization only. References to ailf.team documents in the same dataset.",
4528
+ name: "teams",
4529
+ of: [
4530
+ defineArrayMember2({
4531
+ to: [{ type: "ailf.team" }],
4532
+ type: "reference"
4533
+ })
4534
+ ],
4535
+ readOnly: true,
4536
+ title: "Teams",
4537
+ type: "array"
4538
+ }),
4539
+ defineField7({
4540
+ description: "Per-user UI personalization.",
4541
+ fields: [
4542
+ defineField7({
4543
+ description: "The user's default team \u2014 one of `teams`. Distinct from `teams` so 'which team's view do I default to' can differ from 'all teams I affiliate with'. The slug consumers need is derived in GROQ.",
4544
+ name: "primaryTeam",
4545
+ readOnly: true,
4546
+ title: "Primary Team",
4547
+ to: [{ type: "ailf.team" }],
4548
+ type: "reference"
4549
+ })
4550
+ ],
4551
+ name: "preferences",
4552
+ readOnly: true,
4553
+ title: "Preferences",
4554
+ type: "object"
4555
+ }),
4556
+ defineField7({
4557
+ description: "ISO 8601 UTC \u2014 stamped on each save by the dashboard.",
4558
+ name: "updatedAt",
4559
+ readOnly: true,
4560
+ title: "Updated At",
4561
+ type: "datetime"
4562
+ })
4563
+ ],
4564
+ name: "ailf.user",
4565
+ preview: {
4566
+ prepare({ displayName, email }) {
4567
+ const title = typeof displayName === "string" && displayName ? displayName : typeof email === "string" && email ? email : "(unknown user)";
4568
+ return {
4569
+ subtitle: typeof email === "string" ? email : "",
4570
+ title
4571
+ };
4572
+ },
4573
+ select: {
4574
+ displayName: "displayName",
4575
+ email: "email"
4576
+ }
4577
+ },
4578
+ title: "AILF User",
4579
+ type: "document"
4580
+ });
4581
+
4412
4582
  // src/schema/webhook-config.ts
4413
- import { ALL_FIELDS_GROUP as ALL_FIELDS_GROUP7, defineField as defineField7, defineType as defineType7 } from "sanity";
4414
- var webhookConfigSchema = defineType7({
4583
+ import { ALL_FIELDS_GROUP as ALL_FIELDS_GROUP7, defineField as defineField8, defineType as defineType8 } from "sanity";
4584
+ var webhookConfigSchema = defineType8({
4415
4585
  groups: [
4416
4586
  { name: "main", title: "Main", default: true },
4417
4587
  { name: "optional", title: "Optional" },
4418
4588
  ALL_FIELDS_GROUP7
4419
4589
  ],
4420
4590
  fields: [
4421
- defineField7({
4591
+ defineField8({
4422
4592
  description: "When enabled, publishing articles will automatically trigger AI Literacy evaluations for affected feature areas.",
4423
4593
  group: ["main", "all-fields"],
4424
4594
  initialValue: false,
@@ -4426,7 +4596,7 @@ var webhookConfigSchema = defineType7({
4426
4596
  title: "Evaluate on Publish",
4427
4597
  type: "boolean"
4428
4598
  }),
4429
- defineField7({
4599
+ defineField8({
4430
4600
  description: "Which evaluation mode to use for webhook-triggered evaluations.",
4431
4601
  group: ["main", "all-fields"],
4432
4602
  initialValue: "baseline",
@@ -4442,7 +4612,7 @@ var webhookConfigSchema = defineType7({
4442
4612
  title: "Evaluation Mode",
4443
4613
  type: "string"
4444
4614
  }),
4445
- defineField7({
4615
+ defineField8({
4446
4616
  description: "Maximum evaluations per day. Prevents runaway costs from rapid editing.",
4447
4617
  group: ["main", "all-fields"],
4448
4618
  initialValue: 20,
@@ -4451,7 +4621,7 @@ var webhookConfigSchema = defineType7({
4451
4621
  type: "number",
4452
4622
  validation: (rule) => rule.min(1).max(100)
4453
4623
  }),
4454
- defineField7({
4624
+ defineField8({
4455
4625
  description: "Seconds to wait after the last edit before dispatching. Coalesces rapid edits into a single evaluation.",
4456
4626
  group: ["optional", "all-fields"],
4457
4627
  initialValue: 300,
@@ -4460,7 +4630,7 @@ var webhookConfigSchema = defineType7({
4460
4630
  type: "number",
4461
4631
  validation: (rule) => rule.min(10).max(3600)
4462
4632
  }),
4463
- defineField7({
4633
+ defineField8({
4464
4634
  description: "Specific feature areas to evaluate. Leave empty to evaluate all affected areas automatically.",
4465
4635
  group: ["optional", "all-fields"],
4466
4636
  name: "areas",
@@ -4468,7 +4638,7 @@ var webhookConfigSchema = defineType7({
4468
4638
  title: "Area Filter",
4469
4639
  type: "array"
4470
4640
  }),
4471
- defineField7({
4641
+ defineField8({
4472
4642
  description: "Slack webhook URL for notifications about webhook-triggered evaluations.",
4473
4643
  group: ["optional", "all-fields"],
4474
4644
  name: "notifySlack",
@@ -4615,7 +4785,7 @@ function deriveHelpTopic(routerState) {
4615
4785
  if (routerState.reportId) {
4616
4786
  switch (routerState.tab) {
4617
4787
  case "diagnostics":
4618
- return routerState.subTab === "strengths" ? "interpreting-diagnostics" : "weaknesses-recommendations";
4788
+ return routerState.subTab === "strengths" ? "interpreting-diagnostics" : "recommendations";
4619
4789
  case "activity":
4620
4790
  return "how-agents-work";
4621
4791
  default:
@@ -15813,6 +15983,21 @@ function ailfTeamsStructureItem(S) {
15813
15983
  ])
15814
15984
  );
15815
15985
  }
15986
+ function ailfUsersStructureItem(S) {
15987
+ return S.listItem().id("ailfUsers").title("Users").child(
15988
+ S.list().id("ailfUsersViews").title("Users").items([
15989
+ S.listItem().id("allUsers").title("All users").child(
15990
+ S.documentTypeList("ailf.user").id("ailfUsersAll").title("All users")
15991
+ ),
15992
+ S.divider(),
15993
+ S.listItem().id("usersWithoutTeams").title("\u26A0 Users without teams").icon(WarningOutlineIcon5).child(
15994
+ S.documentTypeList("ailf.user").id("ailfUsersWithoutTeams").title("\u26A0 Users without teams").apiVersion(API_VERSION).filter(
15995
+ '_type == "ailf.user" && (!defined(teams) || count(teams) == 0)'
15996
+ )
15997
+ )
15998
+ ])
15999
+ );
16000
+ }
15816
16001
  function ailfAreasStructureItem(S) {
15817
16002
  return S.listItem().id("ailfAreas").title("Areas").child(
15818
16003
  S.list().id("ailfAreasViews").title("Areas").items([
@@ -15851,13 +16036,14 @@ function ailfChannelsWithUnknownEventsItem(S) {
15851
16036
  var ailfStructure = (S) => S.list().id("root").title("Content").items([
15852
16037
  ailfTaskStructureItem(S),
15853
16038
  ailfTeamsStructureItem(S),
16039
+ ailfUsersStructureItem(S),
15854
16040
  ailfAreasStructureItem(S),
15855
16041
  ailfReportsStructureItem(S),
15856
16042
  ailfChannelsWithUnknownEventsItem(S),
15857
16043
  S.divider(),
15858
16044
  ...S.documentTypeListItems().filter((listItem) => {
15859
16045
  const id = listItem.getId();
15860
- return id !== "ailf.task" && id !== "ailf.team" && id !== "ailf.featureArea" && id !== "ailf.report";
16046
+ return id !== "ailf.task" && id !== "ailf.team" && id !== "ailf.user" && id !== "ailf.featureArea" && id !== "ailf.report";
15861
16047
  })
15862
16048
  ]);
15863
16049
 
@@ -16207,6 +16393,7 @@ var ailfPlugin = definePlugin((options) => ({
16207
16393
  reportSchema,
16208
16394
  taskSchema,
16209
16395
  teamSchema,
16396
+ userSchema,
16210
16397
  webhookConfigSchema
16211
16398
  ]
16212
16399
  },
@@ -16252,5 +16439,6 @@ export {
16252
16439
  taskSchema,
16253
16440
  teamSchema,
16254
16441
  useHelp,
16442
+ userSchema,
16255
16443
  webhookConfigSchema
16256
16444
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ailf-studio",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "AI Literacy Framework — Sanity Studio dashboard plugin",
5
5
  "type": "module",
6
6
  "license": "MIT",