specfuse-authoring 0.3.1__py3-none-any.whl

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.
Files changed (76) hide show
  1. specfuse/authoring/__init__.py +26 -0
  2. specfuse/authoring/_kit/generator.lock +12 -0
  3. specfuse/authoring/_kit/handbooks/.gitkeep +0 -0
  4. specfuse/authoring/_kit/handbooks/AI_Access_Policy_Framework.md +325 -0
  5. specfuse/authoring/_kit/handbooks/API_Handbook.md +2830 -0
  6. specfuse/authoring/_kit/handbooks/Arazzo_Handbook.md +1214 -0
  7. specfuse/authoring/_kit/handbooks/AsyncAPI_Handbook.md +1366 -0
  8. specfuse/authoring/_kit/handbooks/Project_File.md +1058 -0
  9. specfuse/authoring/_kit/handbooks/Vendor_Extensions.md +2427 -0
  10. specfuse/authoring/_kit/samples/.gitkeep +0 -0
  11. specfuse/authoring/_kit/samples/endpoint-samples.yaml +370 -0
  12. specfuse/authoring/_kit/samples/message-samples.yaml +494 -0
  13. specfuse/authoring/_kit/samples/recipe-samples.yaml +909 -0
  14. specfuse/authoring/_kit/samples/scenario-samples.yaml +1133 -0
  15. specfuse/authoring/_kit/schemas/README.md +90 -0
  16. specfuse/authoring/_kit/schemas/arazzo-extensions/specfuse-arazzo-combined.schema.json +213 -0
  17. specfuse/authoring/_kit/schemas/arazzo-extensions/x-actors.schema.json +43 -0
  18. specfuse/authoring/_kit/schemas/arazzo-extensions/x-as.schema.json +8 -0
  19. specfuse/authoring/_kit/schemas/arazzo-extensions/x-async.schema.json +120 -0
  20. specfuse/authoring/_kit/schemas/arazzo-extensions/x-doc.schema.json +47 -0
  21. specfuse/authoring/_kit/schemas/arazzo-extensions/x-mcp.schema.json +45 -0
  22. specfuse/authoring/_kit/schemas/arazzo-extensions/x-recipe.schema.json +40 -0
  23. specfuse/authoring/_kit/schemas/arazzo-extensions/x-sample.schema.json +39 -0
  24. specfuse/authoring/_kit/schemas/arazzo-extensions/x-setup.schema.json +19 -0
  25. specfuse/authoring/_kit/schemas/arazzo-extensions/x-ui.schema.json +83 -0
  26. specfuse/authoring/_kit/schemas/arazzo-extensions/x-version.schema.json +46 -0
  27. specfuse/authoring/_kit/schemas/spectral/functions/arazzoAsActorExists.js +40 -0
  28. specfuse/authoring/_kit/schemas/spectral/functions/arazzoFileHomogeneity.js +43 -0
  29. specfuse/authoring/_kit/schemas/spectral/functions/arazzoRecipeScenarioExclusion.js +85 -0
  30. specfuse/authoring/_kit/schemas/spectral/functions/arazzoSetupForbiddenInRecipes.js +62 -0
  31. specfuse/authoring/_kit/schemas/spectral/functions/arazzoUiSelectorBinding.js +41 -0
  32. specfuse/authoring/_kit/schemas/spectral/functions/asyncAuditableEventEnvelopeShape.js +113 -0
  33. specfuse/authoring/_kit/schemas/spectral/functions/asyncChannelMessageCompleteness.js +70 -0
  34. specfuse/authoring/_kit/schemas/spectral/functions/asyncChannelMessageOrphan.js +54 -0
  35. specfuse/authoring/_kit/schemas/spectral/functions/asyncContextCoherence.js +63 -0
  36. specfuse/authoring/_kit/schemas/spectral/functions/asyncEventNameActionClass.js +81 -0
  37. specfuse/authoring/_kit/schemas/spectral/functions/asyncFirstAppearanceCreated.js +58 -0
  38. specfuse/authoring/_kit/schemas/spectral/functions/asyncInboxDedupCoherence.js +83 -0
  39. specfuse/authoring/_kit/schemas/spectral/functions/asyncSubscriptionFilterEntityCap.js +32 -0
  40. specfuse/authoring/_kit/schemas/spectral/functions/asyncSubscriptionScopingMode.js +84 -0
  41. specfuse/authoring/_kit/schemas/spectral/functions/asyncTriggerWhenCoherence.js +84 -0
  42. specfuse/authoring/_kit/schemas/spectral/specfuse-arazzo.yaml +339 -0
  43. specfuse/authoring/_kit/schemas/spectral/specfuse-asyncapi.yaml +955 -0
  44. specfuse/authoring/_kit/schemas/spectral/specfuse-openapi.yaml +1042 -0
  45. specfuse/authoring/_kit/templates/ai-access-policy-template.md +212 -0
  46. specfuse/authoring/_kit/templates/project-init/.gitignore.template +14 -0
  47. specfuse/authoring/_kit/templates/project-init/CLAUDE.md.template +135 -0
  48. specfuse/authoring/_kit/templates/project-init/api/docs/.gitkeep +0 -0
  49. specfuse/authoring/_kit/templates/project-init/api/specs/v1/async-common/channels/application-events.yaml.template +26 -0
  50. specfuse/authoring/_kit/templates/project-init/api/specs/v1/async-common/message-traits/common.yaml.template +63 -0
  51. specfuse/authoring/_kit/templates/project-init/api/specs/v1/async-common/operation-traits/common.yaml.template +20 -0
  52. specfuse/authoring/_kit/templates/project-init/api/specs/v1/asyncapi.yaml.template +39 -0
  53. specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/enums.yaml.template +35 -0
  54. specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/headers/common.yaml.template +24 -0
  55. specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/parameters/pagination.yaml.template +48 -0
  56. specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/parameters/path.yaml.template +36 -0
  57. specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/responses/errors.yaml.template +112 -0
  58. specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/securitySchemes/auth.yaml.template +15 -0
  59. specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/async-operations/.gitkeep +0 -0
  60. specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/channels/.gitkeep +0 -0
  61. specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/events/.gitkeep +0 -0
  62. specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/messages/.gitkeep +0 -0
  63. specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/models/.gitkeep +0 -0
  64. specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/operations/.gitkeep +0 -0
  65. specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/scenarios/.gitkeep +0 -0
  66. specfuse/authoring/_kit/templates/project-init/api/specs/v1/openapi.yaml.template +63 -0
  67. specfuse/authoring/_kit/templates/project-init/{project-name}-project.json.template +32 -0
  68. specfuse/authoring/bootstrap.py +208 -0
  69. specfuse/authoring/cli.py +62 -0
  70. specfuse/authoring/generator.py +145 -0
  71. specfuse_authoring-0.3.1.dist-info/METADATA +103 -0
  72. specfuse_authoring-0.3.1.dist-info/RECORD +76 -0
  73. specfuse_authoring-0.3.1.dist-info/WHEEL +4 -0
  74. specfuse_authoring-0.3.1.dist-info/entry_points.txt +2 -0
  75. specfuse_authoring-0.3.1.dist-info/licenses/LICENSE +201 -0
  76. specfuse_authoring-0.3.1.dist-info/licenses/NOTICE +6 -0
@@ -0,0 +1,26 @@
1
+ """Specfuse authoring kit — bootstrap, asset refresh, and generator launcher."""
2
+
3
+ from importlib.resources import files
4
+ from pathlib import Path
5
+
6
+ __version__ = "0.3.1"
7
+
8
+
9
+ def kit_root() -> Path:
10
+ """Resolve the directory holding the kit's authored content.
11
+
12
+ Works in two layouts:
13
+ * installed wheel -> specfuse/authoring/_kit/...
14
+ * editable/repo -> <repo-root>/ (handbooks/, samples/, ...)
15
+ """
16
+ bundled = Path(str(files("specfuse.authoring"))) / "_kit"
17
+ if (bundled / "handbooks").is_dir():
18
+ return bundled
19
+ # Dev fallback: specfuse/authoring/__init__.py -> repo root is two parents up.
20
+ repo = Path(__file__).resolve().parents[2]
21
+ if (repo / "handbooks").is_dir():
22
+ return repo
23
+ raise FileNotFoundError(
24
+ "Cannot locate kit content (handbooks/). "
25
+ f"Looked in {bundled} and {repo}."
26
+ )
@@ -0,0 +1,12 @@
1
+ {
2
+ "kit_version": "0.3.1",
3
+ "min_java": 17,
4
+ "generator": {
5
+ "version": "0.1.0",
6
+ "asset": "specfuse-generator-0.1.0.jar",
7
+ "sha256": "35dad9afc7c52b6d46b731e1918725de27c451edd2a0407d2c5025c555959838",
8
+ "release_repo": "Specfuse/generator-dist",
9
+ "release_tag": "v0.1.0"
10
+ },
11
+ "_note": "Pin the kit<->generator contract here. On each generator release: set version/asset/sha256/release_tag, then publish a matching kit patch. sha256=PENDING means no generator is bundled yet; `specfuse-authoring generate` will refuse with a clear message until a real jar is pinned."
12
+ }
File without changes
@@ -0,0 +1,325 @@
1
+ # AI Access Policy Framework
2
+
3
+ This handbook defines the **framework** every Specfuse project uses to decide what AI agents can do with each entity. The project's concrete tier assignments — which specific entities sit in which tier — live in a project-local overlay (see `templates/ai-access-policy-template.md` for the starter shape).
4
+
5
+ The framework drives the `aiAccess` block on `x-entity` (see [`Vendor_Extensions.md §1.1.1`](./Vendor_Extensions.md) for the schema). The generator emits AI-scoped repositories, field allow-lists, and audit hooks from those blocks.
6
+
7
+ ---
8
+
9
+ ## 1) Why a Framework
10
+
11
+ A consistent four-tier framework keeps AI access decisions:
12
+
13
+ - **Auditable** — every entity has a documented tier with a stable shape (`operations`, `ownedBy`, `writableProperties`, `reason`). Reviewers compare like-for-like.
14
+ - **Defaulted to safety** — absence of `aiAccess` means no access. Broadening is explicit; revoking is removal.
15
+ - **Easy to classify new entities** — three questions (read? write? system of record?) place every entity in exactly one tier.
16
+ - **Stable as agents evolve** — adding a new agent doesn't require revisiting every entity, only the ones whose access surface changes.
17
+
18
+ The alternative — ad-hoc `aiAccess` blocks per entity — produces drift, surprises in production, and review fatigue.
19
+
20
+ ---
21
+
22
+ ## 2) The Four Tiers
23
+
24
+ | Tier | Name | `operations` | `ownedBy` | Criteria |
25
+ |------|------|-------------|-----------|----------|
26
+ | **0** | No access | *(omit `aiAccess`)* | n/a | Security/legal-sensitive, admin-only, or no agent needs it |
27
+ | **1** | Reference read | `[read]` | n/a | AI needs it for context/decisions but must never modify it |
28
+ | **2** | Collaborative | varies | `shared` | AI and humans both edit; last-writer-wins with audit |
29
+ | **3** | AI-owned | `[read, create, update]` | `ai` | AI is the system of record; backend writes are reconciled |
30
+
31
+ ### 2.1 Tier 0 — No Access
32
+
33
+ The entity has no `aiAccess` block at all. Generated AI-facing repositories do not expose this entity in any form — agents cannot read, write, list, or even discover it.
34
+
35
+ **When to use:**
36
+ - The entity participates in an authentication or authorization flow that must remain free of AI influence (invitation tokens, password reset records, OAuth state).
37
+ - The entity is an append-only accounting ledger (payment transactions, audit logs, financial reconciliation rows) where AI presence would compromise integrity guarantees.
38
+ - The entity is operationally orthogonal to AI agents (display screens, integration plumbing).
39
+ - No agent has a plausible reason to read it.
40
+
41
+ **Implementation:** simply omit the `aiAccess` block. The `x-entity` block remains; only `aiAccess` is absent.
42
+
43
+ ### 2.2 Tier 1 — Reference Read
44
+
45
+ The agent may read every non-encrypted top-level property of the entity, but cannot create, update, or delete it.
46
+
47
+ ```yaml
48
+ aiAccess:
49
+ operations: [read]
50
+ ```
51
+
52
+ **When to use:**
53
+ - Reference/master data the agent needs as context (tenants, customers, policies, templates).
54
+ - Human-owned data the agent must understand but never edit (employment records, preferences, time-off requests).
55
+ - Anything an agent reads to make decisions about *other* entities (e.g., the agent reads a tenant config to decide how to triage a work item, but never writes the tenant itself).
56
+
57
+ **Encrypted-field default:** when `readableProperties` is omitted, the agent reads every top-level property *except* those listed in `encryptedProperties`. To grant read access to an encrypted field, list it explicitly (see §4 below).
58
+
59
+ **No write access whatsoever.** Tier 1 is purely read-only. If the agent needs to write *any* property, the entity is tier 2 or 3.
60
+
61
+ ### 2.3 Tier 2 — Collaborative (`ownedBy: shared`)
62
+
63
+ Both AI and humans may write. Conflicts are resolved last-writer-wins with audit trail.
64
+
65
+ ```yaml
66
+ aiAccess:
67
+ operations: [read, create, update] # subset varies per entity
68
+ writableProperties:
69
+ - status
70
+ - priority
71
+ - notes
72
+ # ... only the fields the agent is allowed to write
73
+ immutableOnUpdate:
74
+ - tenantId
75
+ - customerId
76
+ ownedBy: shared
77
+ reason: >
78
+ Triage agent classifies and prioritizes incoming items;
79
+ humans curate the queue and adjust outcomes.
80
+ ```
81
+
82
+ **When to use:**
83
+ - Operational entities where the agent assists but doesn't replace humans (work items, comments, activity logs, attachments).
84
+ - Configuration entities the agent helps initialize during onboarding but humans subsequently maintain (settings, templates, schedules).
85
+ - Entities where AI detects state changes and humans react, or vice versa.
86
+
87
+ **The shared write contract:**
88
+ - `writableProperties` is always an explicit allow-list. There is no "all fields" shortcut. Every field the agent is allowed to write must be enumerated.
89
+ - `immutableOnUpdate` is a subset of `writableProperties` covering fields the agent may set on create but never change on update. **Foreign keys to tenant/scope are always immutable on update** — AI must never re-parent a record across tenants.
90
+ - `reason` describes the business need in product language, not implementation language. ("Triage agent classifies incoming items" — not "the triage handler in `TriageWorker.cs` sets the status field").
91
+ - `ownedBy: shared` is the explicit declaration. The default would be inferred but writing it out keeps the tier visible at the point of use.
92
+
93
+ **Operations subset:** tier 2 uses whatever subset of `[read, create, update]` matches the agent's actual needs. Some entities are read+create only (the agent creates new records but never modifies existing ones); some are read+update only (the agent only annotates existing rows). Tailor the subset to the use case.
94
+
95
+ ### 2.4 Tier 3 — AI-Owned (`ownedBy: ai`)
96
+
97
+ The AI agent is the system of record for the entity. Backend writes are advisory and reconciled against agent state.
98
+
99
+ ```yaml
100
+ aiAccess:
101
+ operations: [read, create, update]
102
+ writableProperties:
103
+ - status
104
+ - confidence
105
+ - reasoning
106
+ - dataPoints
107
+ # ... full owned surface
108
+ immutableOnUpdate:
109
+ - tenantId
110
+ ownedBy: ai
111
+ reason: >
112
+ AI artifact. The recommendation agent generates suggestions
113
+ end-to-end based on demand forecasting; humans accept or reject
114
+ but do not author.
115
+ ```
116
+
117
+ **When to use:**
118
+ - AI-generated artifacts that humans consume but don't author: suggestions, predictions, classifications, generated plans, model outputs.
119
+ - Entities whose business semantics are "the AI's view of the world" — overriding from the backend means the AI was wrong, not that humans were.
120
+ - Entities the agent owns end-to-end through their lifecycle (created by AI, transitioned by AI, archived by AI).
121
+
122
+ **The AI-owned contract:**
123
+ - `ownedBy: ai` declares the conflict-resolution authority. When a backend write contradicts agent state, the agent's view wins on reconciliation.
124
+ - The agent's `writableProperties` is typically broader than tier 2 — closer to the full editable surface of the entity — because the agent is the legitimate author of every field except scope/identity.
125
+ - `immutableOnUpdate` still includes scope foreign keys for the same tenant-isolation reason.
126
+ - `reason` makes the ownership explicit: "AI artifact" or "AI owns generation/lifecycle of this entity."
127
+
128
+ **Tier 3 is rare.** Most entities are tier 0, 1, or 2. Tier 3 is reserved for entities whose existence is justified by the agent's work.
129
+
130
+ ---
131
+
132
+ ## 3) Cross-Cutting Rules
133
+
134
+ These rules apply across all tiers and override per-entity convenience.
135
+
136
+ ### 3.1 No `delete` for any tier
137
+
138
+ Soft-delete is the only delete in a Specfuse project (see [`API_Handbook.md §2`](./API_Handbook.md) deletion policy). Revoking records is a human decision: a soft-delete is reversible within the retention window via privileged internal APIs, and hard-delete only happens automatically after retention expires.
139
+
140
+ `aiAccess.operations` MUST NOT include `delete`. If an agent's job is "mark as deleted," express that as `update` on the `status` field (or whichever soft-delete marker the entity uses).
141
+
142
+ ### 3.2 Encrypted fields excluded from implicit read
143
+
144
+ When `readableProperties` is omitted on a tier 1+ entity, the agent reads every top-level property *except* fields listed in `encryptedProperties` (or fields carrying `x-classification: [encrypted]`).
145
+
146
+ To grant AI read access to an encrypted field, list it explicitly in `readableProperties`:
147
+
148
+ ```yaml
149
+ Customer:
150
+ x-entity:
151
+ encryptedProperties: [taxId]
152
+ aiAccess:
153
+ operations: [read]
154
+ readableProperties:
155
+ - firstName
156
+ - lastName
157
+ - email
158
+ - phone
159
+ - taxId # Explicitly granted — not auto-included
160
+ reason: >
161
+ Tax-reconciliation agent needs the tax ID to match
162
+ external accounting system records.
163
+ ```
164
+
165
+ Masking rules on the wire still apply — encrypted fields surface masked unless the caller has the elevated privilege to see plaintext.
166
+
167
+ ### 3.3 Foreign keys are always `immutableOnUpdate`
168
+
169
+ For any tier 2 or tier 3 entity, foreign-key scope fields (`tenantId`, parent-aggregate IDs, etc.) MUST appear in `immutableOnUpdate`. AI must never re-parent a record across tenants or move it under a different parent aggregate — that's a structural change that requires explicit human action.
170
+
171
+ Even if `writableProperties` includes a tenant scope field (so the agent can set it on create), `immutableOnUpdate` blocks the agent from changing it on update.
172
+
173
+ ### 3.4 `reason` describes business need, not technology
174
+
175
+ The `reason` string surfaces in:
176
+ - Generated doc-comments on the AI repository class
177
+ - The project's AI manifest (audit reference)
178
+ - PR review diffs
179
+
180
+ It MUST describe the **business reason** the agent needs this access. It MUST NOT reference:
181
+ - Specific class names, files, modules, or namespaces ("the `TriageWorker.cs` handler sets...")
182
+ - Target language idioms ("the C# repository calls...")
183
+ - Specific AI provider, model, or agent implementation ("the GPT-4 prompt in `triage.prompt.md`...")
184
+
185
+ **Good:** "Triage agent creates work items from detected operational issues and updates status through the lifecycle."
186
+
187
+ **Bad:** "The `TriageHandler` Java class calls `WorkItemRepository.save()` after the Claude prompt returns."
188
+
189
+ The reason should still be true in five years even after the implementation changes three times.
190
+
191
+ ### 3.5 Write requires `writableProperties` + `reason`
192
+
193
+ Whenever `operations` contains any of `create`, `update`, or `delete` (well — `delete` is forbidden per §3.1, so realistically `create` or `update`), both:
194
+ - `writableProperties` (non-empty) — explicit allow-list
195
+ - `reason` (non-empty, ≥ ~40 chars of business rationale)
196
+
197
+ …are required. The Spectral validator enforces this. There is no "all fields" write shortcut; every field must be enumerated.
198
+
199
+ ### 3.6 `aiAccess` only on `x-entity` schemas
200
+
201
+ `aiAccess` may only appear on schemas that carry `x-entity` — i.e., main resource schemas (aggregates and entities). It must NOT appear on:
202
+ - Derivative schemas: `Basic{Resource}`, `New{Resource}`, `Update{Resource}`, `{Resource}List`, `{Resource}SearchRequest`
203
+ - Value-object schemas (`x-value-object`)
204
+ - Anonymous inline schemas
205
+
206
+ The same placement rules as other entity-level metadata.
207
+
208
+ ---
209
+
210
+ ## 4) `ownedBy` Semantics
211
+
212
+ `ownedBy` records the system of record when both AI and backend can write. It does not change *what* can write — only *who wins* when both do.
213
+
214
+ | Value | Conflict Resolution |
215
+ |-------|---------------------|
216
+ | `shared` (default) | Last-writer-wins with audit. The runtime logs both writes; the latest one persists. |
217
+ | `ai` | AI agent is authoritative. Backend writes are reconciled against agent state on the next agent pass. |
218
+ | `backend` | Backend is authoritative. AI writes are advisory and may be overridden by subsequent backend writes. |
219
+
220
+ ### 4.1 When to use `shared`
221
+
222
+ The default for tier 2. Use when:
223
+ - Both AI and humans legitimately edit the entity in normal operation.
224
+ - No clear authority hierarchy — the more recent write reflects the more recent decision.
225
+ - The cost of a missed update is reversible (status revert, comment edit, tag change).
226
+
227
+ ### 4.2 When to use `ai`
228
+
229
+ Use when:
230
+ - The entity exists because the agent produces it.
231
+ - A backend write that contradicts agent state is more likely to be wrong than the agent's view.
232
+ - The agent has a reconciliation loop that re-applies its intended state on the next pass.
233
+
234
+ Tier 3 entities almost always use `ownedBy: ai`. Tier 2 entities rarely do.
235
+
236
+ ### 4.3 When to use `backend`
237
+
238
+ Use when:
239
+ - The backend is the canonical source (e.g., a system the agent has only advisory access to).
240
+ - AI suggestions should appear in the entity but be considered overridable by any backend write.
241
+ - The agent intentionally writes "best-effort" data the backend may correct.
242
+
243
+ This is uncommon. Most tier 2 entities use `shared`; if the backend is fully authoritative, the entity is likely tier 1 (read-only for AI) instead.
244
+
245
+ ---
246
+
247
+ ## 5) Future: Per-Agent Scoping
248
+
249
+ The single `aiAccess` block treats "AI" as monolithic. When a project has multiple agents with different trust levels (a low-trust triage agent and a high-trust orchestration agent both touching `WorkItem`, for example), this becomes a limitation.
250
+
251
+ The framework will extend to allow `aiAccess` keyed by agent role — without breaking the single-block form — when the need surfaces in production usage. Until then, the recommendation is to keep access conservative: the policy reflects the *least-trusted* agent that touches the entity, and the runtime enforces additional restrictions on more-restricted agents at the agent layer rather than the data layer.
252
+
253
+ If your project hits this limitation early, file a kit issue with the specific scenario — it shapes the extension design.
254
+
255
+ ---
256
+
257
+ ## 6) How to Classify a New Entity
258
+
259
+ Three questions, applied in order:
260
+
261
+ 1. **Does any plausible AI capability need to *read* this entity?**
262
+ - No → **Tier 0** (omit `aiAccess`)
263
+ - Yes → continue
264
+
265
+ 2. **Does any plausible AI capability need to *write* it?**
266
+ - No → **Tier 1** (`operations: [read]`)
267
+ - Yes → continue
268
+
269
+ 3. **Is the AI the system of record?**
270
+ - No → **Tier 2** (`ownedBy: shared`)
271
+ - Yes → **Tier 3** (`ownedBy: ai`)
272
+
273
+ ### 6.1 When in doubt, start more restrictive
274
+
275
+ Broadening access later is cheap (add fields to `writableProperties`, expand `operations`, raise the tier). Revoking access after agents depend on it is not — agents will have built workflows on top of the access, and removing it breaks them.
276
+
277
+ Default to:
278
+ - Tier 0 if you're unsure whether *any* agent will ever need it.
279
+ - Tier 1 if you know an agent will read it but you're unsure about writes.
280
+ - Tier 2 with the narrowest possible `writableProperties` if writes are needed.
281
+
282
+ The flowchart is biased toward restriction by design.
283
+
284
+ ### 6.2 Edge cases
285
+
286
+ **The agent reads the entity to make decisions about another entity.** → Tier 1. Reading another entity to inform a write doesn't elevate this entity's tier.
287
+
288
+ **The agent creates the entity but never updates it.** → Tier 2 with `operations: [read, create]` (no `update`). The shared-write contract still applies, but `writableProperties` lists only the fields the agent populates on create.
289
+
290
+ **The agent creates suggestions that other code converts into "real" entities.** → The suggestion entity is tier 3 (`ownedBy: ai`). The downstream entity is tier 2 or whatever its independent classification dictates. They are separate decisions.
291
+
292
+ **An entity has both an AI-owned subset and a human-owned subset of fields.** → Tier 2 with `writableProperties` restricted to the AI-owned subset. The framework doesn't have a "tier 2.5" — explicit `writableProperties` is the lever.
293
+
294
+ **An entity is read by AI for now, but the project plans to add write access in six months.** → Tier 1 now. Revisit when the write access is actually needed. Don't pre-grant write capability "because we'll need it later" — the framework's safety stance is least-privilege at all times.
295
+
296
+ ---
297
+
298
+ ## 7) Relationship to Other Extensions
299
+
300
+ | Extension | Relationship |
301
+ |---|---|
302
+ | `x-entity` | `aiAccess` is a property of `x-entity`. See [`Vendor_Extensions.md §1.1.1`](./Vendor_Extensions.md). |
303
+ | `encryptedProperties` / `x-classification: [encrypted]` | Encrypted fields are excluded from the implicit read surface (§3.2). |
304
+ | `x-classification: [pii \| sensitive]` | PII/sensitive fields are not auto-excluded from AI read, but they trigger snapshot acknowledgement rules (`x-snapshot-pii-acknowledged`) when included in event snapshots. See `AsyncAPI_Handbook.md §2.3`. |
305
+ | `x-ai-safe` (operations) | Operates at the HTTP operation level — whether an AI agent may invoke a specific endpoint without approval. `aiAccess` operates at the entity/repository level — what the AI may touch via generated data-access code. The two are complementary; a tier 2 entity may still have endpoints that are not `x-ai-safe`. |
306
+ | `x-ai.entities` (AsyncAPI workers) | When an AsyncAPI worker declares `x-ai.entities.{reads,creates,updates,deletes}`, every listed entity MUST have a matching `aiAccess` block granting the corresponding operation. The cross-spec validator enforces this. See `AsyncAPI_Handbook.md §4.3`. |
307
+ | `filterableProperties` / `searchableProperties` | Govern HTTP query surfaces, not AI repository methods. AI filtering/search allow-lists are derived separately from `readableProperties`. |
308
+
309
+ ---
310
+
311
+ ## 8) Where to Find the Project's Tier Assignments
312
+
313
+ This handbook defines the framework. The project's concrete per-entity tier assignments live in a project-local document, typically `api/docs/{project}-ai-access-matrix.md` or similar — the kit provides a starter in [`templates/ai-access-policy-template.md`](../templates/ai-access-policy-template.md) which the project copies and fills in.
314
+
315
+ The matrix document lists every entity in the project and assigns it to a tier, with the YAML `aiAccess` block that gets added to its `x-entity`. It is the team-visible reference for what AI agents can do; the framework above is how those tiers are reasoned about.
316
+
317
+ When a new entity is added to the project, the team:
318
+ 1. Applies the classification flowchart (§6) to pick a tier.
319
+ 2. Updates the matrix document with the entity, its tier, and rationale.
320
+ 3. Adds the corresponding `aiAccess` block to the entity's `x-entity` in the OpenAPI spec.
321
+ 4. The Spectral validator enforces that the tier-shape and aiAccess-block-shape are consistent.
322
+
323
+ ---
324
+
325
+ *This handbook is loaded as mandatory reading via `CLAUDE.md`. All `aiAccess` blocks in OpenAPI specs must comply with the framework defined here.*