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.
- specfuse/authoring/__init__.py +26 -0
- specfuse/authoring/_kit/generator.lock +12 -0
- specfuse/authoring/_kit/handbooks/.gitkeep +0 -0
- specfuse/authoring/_kit/handbooks/AI_Access_Policy_Framework.md +325 -0
- specfuse/authoring/_kit/handbooks/API_Handbook.md +2830 -0
- specfuse/authoring/_kit/handbooks/Arazzo_Handbook.md +1214 -0
- specfuse/authoring/_kit/handbooks/AsyncAPI_Handbook.md +1366 -0
- specfuse/authoring/_kit/handbooks/Project_File.md +1058 -0
- specfuse/authoring/_kit/handbooks/Vendor_Extensions.md +2427 -0
- specfuse/authoring/_kit/samples/.gitkeep +0 -0
- specfuse/authoring/_kit/samples/endpoint-samples.yaml +370 -0
- specfuse/authoring/_kit/samples/message-samples.yaml +494 -0
- specfuse/authoring/_kit/samples/recipe-samples.yaml +909 -0
- specfuse/authoring/_kit/samples/scenario-samples.yaml +1133 -0
- specfuse/authoring/_kit/schemas/README.md +90 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/specfuse-arazzo-combined.schema.json +213 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-actors.schema.json +43 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-as.schema.json +8 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-async.schema.json +120 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-doc.schema.json +47 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-mcp.schema.json +45 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-recipe.schema.json +40 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-sample.schema.json +39 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-setup.schema.json +19 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-ui.schema.json +83 -0
- specfuse/authoring/_kit/schemas/arazzo-extensions/x-version.schema.json +46 -0
- specfuse/authoring/_kit/schemas/spectral/functions/arazzoAsActorExists.js +40 -0
- specfuse/authoring/_kit/schemas/spectral/functions/arazzoFileHomogeneity.js +43 -0
- specfuse/authoring/_kit/schemas/spectral/functions/arazzoRecipeScenarioExclusion.js +85 -0
- specfuse/authoring/_kit/schemas/spectral/functions/arazzoSetupForbiddenInRecipes.js +62 -0
- specfuse/authoring/_kit/schemas/spectral/functions/arazzoUiSelectorBinding.js +41 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncAuditableEventEnvelopeShape.js +113 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncChannelMessageCompleteness.js +70 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncChannelMessageOrphan.js +54 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncContextCoherence.js +63 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncEventNameActionClass.js +81 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncFirstAppearanceCreated.js +58 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncInboxDedupCoherence.js +83 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncSubscriptionFilterEntityCap.js +32 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncSubscriptionScopingMode.js +84 -0
- specfuse/authoring/_kit/schemas/spectral/functions/asyncTriggerWhenCoherence.js +84 -0
- specfuse/authoring/_kit/schemas/spectral/specfuse-arazzo.yaml +339 -0
- specfuse/authoring/_kit/schemas/spectral/specfuse-asyncapi.yaml +955 -0
- specfuse/authoring/_kit/schemas/spectral/specfuse-openapi.yaml +1042 -0
- specfuse/authoring/_kit/templates/ai-access-policy-template.md +212 -0
- specfuse/authoring/_kit/templates/project-init/.gitignore.template +14 -0
- specfuse/authoring/_kit/templates/project-init/CLAUDE.md.template +135 -0
- specfuse/authoring/_kit/templates/project-init/api/docs/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/async-common/channels/application-events.yaml.template +26 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/async-common/message-traits/common.yaml.template +63 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/async-common/operation-traits/common.yaml.template +20 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/asyncapi.yaml.template +39 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/enums.yaml.template +35 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/headers/common.yaml.template +24 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/parameters/pagination.yaml.template +48 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/parameters/path.yaml.template +36 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/responses/errors.yaml.template +112 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/common/securitySchemes/auth.yaml.template +15 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/async-operations/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/channels/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/events/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/messages/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/models/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/operations/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/domains/{initial-domain}/scenarios/.gitkeep +0 -0
- specfuse/authoring/_kit/templates/project-init/api/specs/v1/openapi.yaml.template +63 -0
- specfuse/authoring/_kit/templates/project-init/{project-name}-project.json.template +32 -0
- specfuse/authoring/bootstrap.py +208 -0
- specfuse/authoring/cli.py +62 -0
- specfuse/authoring/generator.py +145 -0
- specfuse_authoring-0.3.1.dist-info/METADATA +103 -0
- specfuse_authoring-0.3.1.dist-info/RECORD +76 -0
- specfuse_authoring-0.3.1.dist-info/WHEEL +4 -0
- specfuse_authoring-0.3.1.dist-info/entry_points.txt +2 -0
- specfuse_authoring-0.3.1.dist-info/licenses/LICENSE +201 -0
- 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.*
|