@ordinatio/entities 1.0.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/LICENSE +21 -0
- package/README.md +328 -0
- package/dist/chunk-QXBSEBJW.mjs +750 -0
- package/dist/index.mjs +2265 -0
- package/dist/observer-YUTEGG4S.mjs +10 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ordinatio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# @ordinatio/entities
|
|
2
|
+
|
|
3
|
+
Entity knowledge, agent intelligence, notes, and contacts — the knowledge layer for any Ordinatio application.
|
|
4
|
+
|
|
5
|
+
## What's in the box
|
|
6
|
+
|
|
7
|
+
| Module | Models | Purpose |
|
|
8
|
+
|--------|--------|---------|
|
|
9
|
+
| **Knowledge** | `EntityFieldDefinition`, `KnowledgeLedgerEntry`, `SearchQueryLog`, `SearchPattern` | Structured entity knowledge with immutable append-only ledger |
|
|
10
|
+
| **Knowledge+** | Time-travel, truth scoring, decay, branching, shadow graph, reflection, observer, ghost fields, health | Active knowledge reasoning — the module *thinks about what it knows* |
|
|
11
|
+
| **Agent** | `AgentInteraction`, `AgentSuggestion`, `AgentKnowledge`, `AgentPreference` | Agent intelligence: knowledge base, interaction analytics, proactive suggestions |
|
|
12
|
+
| **Notes** | `Note`, `NoteAttachment` | Entity-agnostic notes with attachments |
|
|
13
|
+
| **Contacts** | `Contact`, `ContactTag` | Contact CRUD with find-or-create for email sync |
|
|
14
|
+
|
|
15
|
+
12 Prisma models, 1 enum, 45 error codes, 409 tests.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @ordinatio/entities
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or via Domus:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx ordinatio add entities
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Standalone
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { PrismaClient } from '@prisma/client';
|
|
35
|
+
import {
|
|
36
|
+
getFieldDefinitions,
|
|
37
|
+
createFieldDefinition,
|
|
38
|
+
setEntityFields,
|
|
39
|
+
getEntityFields,
|
|
40
|
+
createNote,
|
|
41
|
+
getAllContacts,
|
|
42
|
+
createContact,
|
|
43
|
+
logInteraction,
|
|
44
|
+
queryKnowledge,
|
|
45
|
+
} from '@ordinatio/entities';
|
|
46
|
+
|
|
47
|
+
const db = new PrismaClient();
|
|
48
|
+
|
|
49
|
+
// --- Entity Knowledge ---
|
|
50
|
+
// Define what fields an entity type can have
|
|
51
|
+
await createFieldDefinition(db, {
|
|
52
|
+
entityType: 'client',
|
|
53
|
+
key: 'preferred_fabric',
|
|
54
|
+
label: 'Preferred Fabric',
|
|
55
|
+
dataType: 'text',
|
|
56
|
+
category: 'preferences',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Set field values (immutable ledger — old values are superseded, never deleted)
|
|
60
|
+
await setEntityFields(db, 'client', 'client-123', {
|
|
61
|
+
preferred_fabric: 'Loro Piana cashmere',
|
|
62
|
+
budget_range: '$5000-8000',
|
|
63
|
+
}, 'agent', 'interaction-456');
|
|
64
|
+
|
|
65
|
+
// Read current values
|
|
66
|
+
const fields = await getEntityFields(db, 'client', 'client-123');
|
|
67
|
+
|
|
68
|
+
// --- Notes ---
|
|
69
|
+
await createNote(db, {
|
|
70
|
+
entityType: 'client',
|
|
71
|
+
entityId: 'client-123',
|
|
72
|
+
content: 'Prefers navy blue suits for business meetings.',
|
|
73
|
+
source: 'AGENT',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// --- Contacts ---
|
|
77
|
+
const contact = await createContact(db, {
|
|
78
|
+
email: 'john@example.com',
|
|
79
|
+
name: 'John Doe',
|
|
80
|
+
source: 'MANUAL',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// --- Agent Intelligence ---
|
|
84
|
+
await logInteraction(db, {
|
|
85
|
+
userId: 'user-1',
|
|
86
|
+
query: 'Show me overdue orders',
|
|
87
|
+
toolsUsed: ['listOrders', 'getOverdueTasks'],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const knowledge = await queryKnowledge(db, {
|
|
91
|
+
entity: 'fabric',
|
|
92
|
+
field: 'type',
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### With Domus
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { createDomus } from '@ordinatio/domus';
|
|
100
|
+
|
|
101
|
+
const app = await createDomus({
|
|
102
|
+
modules: ['entities'],
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const fields = await app.entities.getEntityFields('client', 'client-123');
|
|
106
|
+
await app.entities.createNote({ entityType: 'client', entityId: 'client-123', content: '...' });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API Reference
|
|
110
|
+
|
|
111
|
+
### Knowledge Module
|
|
112
|
+
|
|
113
|
+
| Function | Type | Description |
|
|
114
|
+
|----------|------|-------------|
|
|
115
|
+
| `getFieldDefinitions(db, entityType?, status?)` | Query | List field definitions |
|
|
116
|
+
| `getFieldDefinitionById(db, id)` | Query | Get a single field definition |
|
|
117
|
+
| `createFieldDefinition(db, input, callbacks?)` | Mutation | Create a field definition |
|
|
118
|
+
| `updateFieldDefinition(db, id, input, callbacks?)` | Mutation | Update a field definition |
|
|
119
|
+
| `getEntityFields(db, entityType, entityId)` | Query | Get current field values for an entity |
|
|
120
|
+
| `setEntityFields(db, entityType, entityId, fields, source, sourceId?, confidence?, setBy?, callbacks?)` | Mutation | Set field values (append-only ledger) |
|
|
121
|
+
| `getFieldHistory(db, entityType, entityId, fieldId?, limit?)` | Query | Get historical field values |
|
|
122
|
+
| `searchByFields(db, entityType, filters, limit?)` | Query | Search entities by field values |
|
|
123
|
+
| `logSearchQuery(db, input)` | Mutation | Log a search query (best-effort) |
|
|
124
|
+
| `getWeekOfYear(date)` | Pure | Calculate ISO week number |
|
|
125
|
+
|
|
126
|
+
### Knowledge+ (Active Reasoning)
|
|
127
|
+
|
|
128
|
+
| Function | Type | Description |
|
|
129
|
+
|----------|------|-------------|
|
|
130
|
+
| `getKnowledgeAt(db, entityType, entityId, timestamp)` | Query | Reconstruct entity state at a point in time |
|
|
131
|
+
| `getFieldValueAt(db, entityType, entityId, fieldKey, timestamp)` | Query | Single field value at a point in time |
|
|
132
|
+
| `getKnowledgeTimeline(db, entityType, entityId, from, to, limit?)` | Query | Timeline of all changes in a date range |
|
|
133
|
+
| `computeTruthScore(assertions, now?)` | Pure | Weighted truth score: T = Σ(C×R×T) / Σ(C×R) |
|
|
134
|
+
| `computeRecencyFactor(createdAt, now?, maxAgeDays?)` | Pure | Exponential recency decay (1.0 → 0) |
|
|
135
|
+
| `computeComplexityScore(fieldsUsed, fieldsAvailable, categoriesUsed, maxCategories)` | Pure | Entity knowledge completeness score |
|
|
136
|
+
| `computeEntityTruthScores(db, entityType, entityId)` | Query | Truth scores for all fields on an entity |
|
|
137
|
+
| `computeDecayedConfidence(confidence, createdAt, halfLifeDays, now?)` | Pure | Exponential confidence decay: C × 0.5^(age/halfLife) |
|
|
138
|
+
| `isStale(confidence, createdAt, halfLifeDays, threshold?, now?)` | Pure | Check if decayed confidence is below threshold |
|
|
139
|
+
| `getStaleFields(db, entityType, entityId, threshold?)` | Query | All stale fields for an entity |
|
|
140
|
+
| `formatStalenessWarnings(staleFields)` | Pure | Agent-friendly staleness warning strings |
|
|
141
|
+
| `shouldBranch(newConfidence, existingConfidence?, threshold?)` | Pure | Should this write trigger validation? |
|
|
142
|
+
| `setEntityFieldsWithBranching(db, entityType, entityId, fields, source, confidence, options?)` | Mutation | Set fields with human-in-the-loop validation |
|
|
143
|
+
| `findRelatedEntities(db, entityType, entityId, options?)` | Query | Discover implicit relationships via shared field values |
|
|
144
|
+
| `getEntityRelationships(db, entityType, entityId, limit?)` | Query | Get all relationships for an entity |
|
|
145
|
+
| `computeRelationshipStrength(sharedFields, totalFieldsOnSource)` | Pure | Relationship strength from shared fields |
|
|
146
|
+
| `detectConflicts(db, entityType, entityId, rules?)` | Query | Check entity for contradictions |
|
|
147
|
+
| `scanForConflicts(db, entityType, rules?, limit?)` | Query | Batch contradiction scan across all entities |
|
|
148
|
+
| `evaluateConflictRule(rule, fields)` | Pure | Evaluate a single conflict rule |
|
|
149
|
+
| `evaluateConstraint(constraint, entityFields)` | Pure | Evaluate a single field constraint |
|
|
150
|
+
| `checkConstraints(db, entityType, entityId)` | Query | Check all constraints for an entity |
|
|
151
|
+
| `fireObservers(db, entityType, entityId, writtenFields, callbacks?)` | Mutation | Fire constraint observers after writes |
|
|
152
|
+
| `predictGhostFields(db, entityType, entityId, options?)` | Query | Predict missing fields from co-occurrence |
|
|
153
|
+
| `writeGhostFields(db, entityType, entityId, predictions, callbacks?)` | Mutation | Write predicted values with low confidence |
|
|
154
|
+
| `buildCoOccurrenceMap(db, entityType, fieldKeyA, fieldKeyB, minOccurrences?)` | Query | Co-occurrence statistics between two fields |
|
|
155
|
+
| `computeOverallScore(completeness, freshness, conflictRate, truthAverage)` | Pure | Weighted composite health score |
|
|
156
|
+
| `computeEntityHealth(db, entityType, entityId, conflictRules?)` | Query | Full health report for one entity |
|
|
157
|
+
| `getEntityTypeHealth(db, entityType, options?)` | Query | Aggregate health across all entities of a type |
|
|
158
|
+
|
|
159
|
+
### Agent Module
|
|
160
|
+
|
|
161
|
+
| Function | Type | Description |
|
|
162
|
+
|----------|------|-------------|
|
|
163
|
+
| `queryKnowledge(db, input, seedProvider?)` | Query | Query agent knowledge base |
|
|
164
|
+
| `createKnowledgeEntry(db, input)` | Mutation | Add a knowledge entry |
|
|
165
|
+
| `updateKnowledgeEntry(db, id, input)` | Mutation | Update a knowledge entry |
|
|
166
|
+
| `deleteKnowledgeEntry(db, id)` | Mutation | Delete a knowledge entry |
|
|
167
|
+
| `ensureKnowledgeDefaults(db, seedProvider?)` | Mutation | Seed defaults (idempotent) |
|
|
168
|
+
| `resetKnowledgeDefaults(db, seedProvider?)` | Mutation | Reset to seed data |
|
|
169
|
+
| `getPreferences(db, input)` | Query | Get agent preferences |
|
|
170
|
+
| `setPreference(db, input)` | Mutation | Set a preference (upsert) |
|
|
171
|
+
| `deletePreference(db, id)` | Mutation | Delete a preference |
|
|
172
|
+
| `logInteraction(db, input)` | Mutation | Log an agent interaction |
|
|
173
|
+
| `markSatisfied(db, id, satisfied)` | Mutation | Mark interaction satisfaction |
|
|
174
|
+
| `getTopicDistribution(db, days?)` | Query | Topic distribution stats |
|
|
175
|
+
| `getRecentInteractions(db, userId, limit?)` | Query | Recent interactions |
|
|
176
|
+
| `getInteractionCount(db, days?)` | Query | Interaction count |
|
|
177
|
+
| `classifyIntent(query)` | Pure | Classify query intent |
|
|
178
|
+
| `extractTopic(query)` | Pure | Extract topic from query |
|
|
179
|
+
| `extractModules(toolNames)` | Pure | Map tools to modules |
|
|
180
|
+
| `analyzeAndSuggest(db)` | Mutation | Generate suggestions from patterns |
|
|
181
|
+
| `getSuggestions(db, status?)` | Query | Get suggestions |
|
|
182
|
+
| `dismissSuggestion(db, id, userId)` | Mutation | Dismiss a suggestion |
|
|
183
|
+
| `approveSuggestion(db, id)` | Mutation | Approve a suggestion |
|
|
184
|
+
|
|
185
|
+
### Notes Module
|
|
186
|
+
|
|
187
|
+
| Function | Type | Description |
|
|
188
|
+
|----------|------|-------------|
|
|
189
|
+
| `createNote(db, input, callbacks?)` | Mutation | Create a note with optional attachments |
|
|
190
|
+
| `updateNote(db, noteId, entityId, input, callbacks?)` | Mutation | Update a note |
|
|
191
|
+
| `getNotes(db, options)` | Query | List notes with cursor pagination |
|
|
192
|
+
| `deleteNote(db, noteId, entityId)` | Mutation | Delete a note |
|
|
193
|
+
|
|
194
|
+
### Contacts Module
|
|
195
|
+
|
|
196
|
+
| Function | Type | Description |
|
|
197
|
+
|----------|------|-------------|
|
|
198
|
+
| `getAllContacts(db, options?)` | Query | List contacts with filters |
|
|
199
|
+
| `getContactById(db, id)` | Query | Get a contact by ID |
|
|
200
|
+
| `getContactByEmail(db, email)` | Query | Find contact by email |
|
|
201
|
+
| `createContact(db, params, callbacks?)` | Mutation | Create a contact |
|
|
202
|
+
| `updateContact(db, id, data)` | Mutation | Update a contact |
|
|
203
|
+
| `deleteContact(db, id)` | Mutation | Delete a contact |
|
|
204
|
+
| `findOrCreateContact(db, email, name?, source?)` | Mutation | Find or create by email |
|
|
205
|
+
|
|
206
|
+
## Callback Injection
|
|
207
|
+
|
|
208
|
+
Mutations accept optional callbacks for app-specific side effects:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import type { MutationCallbacks } from '@ordinatio/entities';
|
|
212
|
+
|
|
213
|
+
const callbacks: MutationCallbacks = {
|
|
214
|
+
logActivity: async (action, description, data) => {
|
|
215
|
+
// Wire to your activity/audit system
|
|
216
|
+
await myActivityService.log(action, description, data);
|
|
217
|
+
},
|
|
218
|
+
emitEvent: async (type, data) => {
|
|
219
|
+
// Wire to your event bus
|
|
220
|
+
await myEventBus.emit(type, data);
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
await createFieldDefinition(db, input, callbacks);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Notes support extended callbacks for structured knowledge extraction:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import type { NoteKnowledgeCallbacks } from '@ordinatio/entities';
|
|
231
|
+
|
|
232
|
+
const noteCallbacks: NoteKnowledgeCallbacks = {
|
|
233
|
+
logActivity: async (action, description) => { /* ... */ },
|
|
234
|
+
setEntityFields: async (entityType, entityId, fields, source) => {
|
|
235
|
+
// Auto-extract structured knowledge from notes
|
|
236
|
+
await setEntityFields(db, entityType, entityId, fields, source);
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
await createNote(db, { entityType: 'client', entityId: 'c1', content: '...' }, noteCallbacks);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Error Codes
|
|
244
|
+
|
|
245
|
+
### KNOWLEDGE_300–361 (Entity Knowledge)
|
|
246
|
+
|
|
247
|
+
| Code | Description |
|
|
248
|
+
|------|-------------|
|
|
249
|
+
| `KNOWLEDGE_300` | Failed to list field definitions |
|
|
250
|
+
| `KNOWLEDGE_301` | Failed to create field definition |
|
|
251
|
+
| `KNOWLEDGE_302` | Failed to update field definition |
|
|
252
|
+
| `KNOWLEDGE_303` | Field definition not found |
|
|
253
|
+
| `KNOWLEDGE_304` | Failed to deactivate field definition |
|
|
254
|
+
| `KNOWLEDGE_305` | Failed to search by field values |
|
|
255
|
+
| `KNOWLEDGE_310` | Failed to get entity fields |
|
|
256
|
+
| `KNOWLEDGE_311` | Failed to set entity fields |
|
|
257
|
+
| `KNOWLEDGE_312` | Failed to get field history |
|
|
258
|
+
| `KNOWLEDGE_313` | Unknown field key |
|
|
259
|
+
| `KNOWLEDGE_314` | Failed to supersede old ledger entry |
|
|
260
|
+
| `KNOWLEDGE_320` | Failed to log search query |
|
|
261
|
+
| `KNOWLEDGE_321` | Failed to process search query log |
|
|
262
|
+
| `KNOWLEDGE_330` | Knowledge batch job failed |
|
|
263
|
+
| `KNOWLEDGE_331` | Failed to extract fields from notes |
|
|
264
|
+
| `KNOWLEDGE_332` | Failed to build temporal patterns |
|
|
265
|
+
| `KNOWLEDGE_340` | Failed to reconstruct knowledge at point in time |
|
|
266
|
+
| `KNOWLEDGE_341` | Invalid date range for timeline query |
|
|
267
|
+
| `KNOWLEDGE_342` | Failed to compute stale fields |
|
|
268
|
+
| `KNOWLEDGE_343` | Failed to write fields with branching |
|
|
269
|
+
| `KNOWLEDGE_344` | Validation callback failed during branching |
|
|
270
|
+
| `KNOWLEDGE_345` | Failed to discover entity relationships |
|
|
271
|
+
| `KNOWLEDGE_346` | Failed to detect contradictions |
|
|
272
|
+
| `KNOWLEDGE_347` | Failed to scan entities for contradictions |
|
|
273
|
+
| `KNOWLEDGE_348` | Failed to compute truth scores |
|
|
274
|
+
| `KNOWLEDGE_350` | Failed to evaluate field constraints |
|
|
275
|
+
| `KNOWLEDGE_351` | Observer callback failed |
|
|
276
|
+
| `KNOWLEDGE_352` | Invalid constraint definition |
|
|
277
|
+
| `KNOWLEDGE_355` | Failed to predict ghost fields |
|
|
278
|
+
| `KNOWLEDGE_356` | Failed to write ghost fields |
|
|
279
|
+
| `KNOWLEDGE_360` | Failed to compute entity health |
|
|
280
|
+
| `KNOWLEDGE_361` | Failed to compute entity type health |
|
|
281
|
+
|
|
282
|
+
### AGENTKNOW_400–412 (Agent Intelligence)
|
|
283
|
+
|
|
284
|
+
| Code | Description |
|
|
285
|
+
|------|-------------|
|
|
286
|
+
| `AGENTKNOW_400` | Failed to query knowledge |
|
|
287
|
+
| `AGENTKNOW_401` | Knowledge entry not found |
|
|
288
|
+
| `AGENTKNOW_402` | Failed to create knowledge entry |
|
|
289
|
+
| `AGENTKNOW_403` | Failed to update knowledge entry |
|
|
290
|
+
| `AGENTKNOW_404` | Failed to delete knowledge entry |
|
|
291
|
+
| `AGENTKNOW_405` | Failed to reset knowledge defaults |
|
|
292
|
+
| `AGENTKNOW_406` | Failed to get preferences |
|
|
293
|
+
| `AGENTKNOW_407` | Failed to set preference |
|
|
294
|
+
| `AGENTKNOW_408` | Failed to delete preference |
|
|
295
|
+
| `AGENTKNOW_409` | Failed to log interaction |
|
|
296
|
+
| `AGENTKNOW_410` | Failed to get suggestions |
|
|
297
|
+
| `AGENTKNOW_411` | Failed to dismiss suggestion |
|
|
298
|
+
| `AGENTKNOW_412` | Failed to approve suggestion |
|
|
299
|
+
|
|
300
|
+
## Prisma Schema
|
|
301
|
+
|
|
302
|
+
The standalone schema fragment is at `packages/ordinatio-domus/src/schema/entities.prisma` (12 models, 1 enum). Cross-domain foreign keys (to User, Client, Tag, EmailMessage) become plain `String?` for standalone use.
|
|
303
|
+
|
|
304
|
+
## Tests
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
pnpm --filter @ordinatio/entities test:run
|
|
308
|
+
# 409 tests across 20 files
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Pugil Integration
|
|
312
|
+
|
|
313
|
+
This package includes a Pugil reporter that generates Council-consumable `trial_report` artifacts from test results.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Normal test run (no Pugil overhead)
|
|
317
|
+
pnpm --filter @ordinatio/entities test:run
|
|
318
|
+
|
|
319
|
+
# With Pugil trial report generation
|
|
320
|
+
PUGIL_ENABLED=true pnpm --filter @ordinatio/entities test:run
|
|
321
|
+
|
|
322
|
+
# With Council cycle integration
|
|
323
|
+
PUGIL_ENABLED=true PUGIL_CYCLE_ID=cycle-entities-v1 pnpm --filter @ordinatio/entities test:run
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
- **Config:** `src/pugil.config.ts` — maps test files to categories (unit, integration, adversarial, chaos, concurrency)
|
|
327
|
+
- **Reporter:** `src/pugil-reporter.ts` — Vitest custom reporter, writes to `pugil-reports/`
|
|
328
|
+
- **Types:** `PugilTestResult`, `PugilTestCategory` from `@ordinatio/core`
|