@gitgov/core 1.3.0 → 1.4.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/README.md +1 -1
- package/dist/src/index.d.ts +2090 -515
- package/dist/src/index.js +1694 -725
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -76,17 +76,19 @@ var actor_record_schema_default = {
|
|
|
76
76
|
},
|
|
77
77
|
publicKey: {
|
|
78
78
|
type: "string",
|
|
79
|
-
|
|
79
|
+
minLength: 44,
|
|
80
|
+
maxLength: 44,
|
|
81
|
+
description: "The Ed25519 public key (base64 encoded, 44 characters) for verifying the actor's signatures."
|
|
80
82
|
},
|
|
81
83
|
roles: {
|
|
82
84
|
type: "array",
|
|
83
85
|
items: {
|
|
84
86
|
type: "string",
|
|
85
|
-
pattern: "^[a-z0-9:]
|
|
87
|
+
pattern: "^[a-z0-9-]+(:[a-z0-9-]+)*$"
|
|
86
88
|
},
|
|
87
89
|
minItems: 1,
|
|
88
90
|
uniqueItems: true,
|
|
89
|
-
description: "List of capacity roles defining the actor's skills and permissions.",
|
|
91
|
+
description: "List of capacity roles defining the actor's skills and permissions. Uses hierarchical format with colons.",
|
|
90
92
|
examples: [
|
|
91
93
|
[
|
|
92
94
|
"developer:backend:go",
|
|
@@ -104,7 +106,7 @@ var actor_record_schema_default = {
|
|
|
104
106
|
"revoked"
|
|
105
107
|
],
|
|
106
108
|
default: "active",
|
|
107
|
-
description: "The lifecycle status of the actor."
|
|
109
|
+
description: "Optional. The lifecycle status of the actor. Defaults to 'active' if not specified."
|
|
108
110
|
},
|
|
109
111
|
supersededBy: {
|
|
110
112
|
type: "string",
|
|
@@ -136,7 +138,6 @@ var agent_record_schema_default = {
|
|
|
136
138
|
type: "object",
|
|
137
139
|
required: [
|
|
138
140
|
"id",
|
|
139
|
-
"guild",
|
|
140
141
|
"engine"
|
|
141
142
|
],
|
|
142
143
|
properties: {
|
|
@@ -153,18 +154,10 @@ var agent_record_schema_default = {
|
|
|
153
154
|
],
|
|
154
155
|
default: "active"
|
|
155
156
|
},
|
|
156
|
-
guild: {
|
|
157
|
-
type: "string",
|
|
158
|
-
enum: [
|
|
159
|
-
"design",
|
|
160
|
-
"intelligence",
|
|
161
|
-
"strategy",
|
|
162
|
-
"operations",
|
|
163
|
-
"quality"
|
|
164
|
-
]
|
|
165
|
-
},
|
|
166
157
|
triggers: {
|
|
167
158
|
type: "array",
|
|
159
|
+
default: [],
|
|
160
|
+
description: "Optional list of triggers that activate the agent.\nAdditional fields are allowed and depend on trigger type:\n- webhook triggers: 'event' (event identifier), 'filter' (condition)\n- scheduled triggers: 'cron' (cron expression)\n- manual triggers: 'command' (example CLI command)\n",
|
|
168
161
|
items: {
|
|
169
162
|
type: "object",
|
|
170
163
|
properties: {
|
|
@@ -174,18 +167,22 @@ var agent_record_schema_default = {
|
|
|
174
167
|
"manual",
|
|
175
168
|
"webhook",
|
|
176
169
|
"scheduled"
|
|
177
|
-
]
|
|
170
|
+
],
|
|
171
|
+
description: "Type of trigger that activates the agent"
|
|
178
172
|
}
|
|
179
173
|
},
|
|
180
174
|
required: [
|
|
181
175
|
"type"
|
|
182
|
-
]
|
|
176
|
+
],
|
|
177
|
+
additionalProperties: true
|
|
183
178
|
}
|
|
184
179
|
},
|
|
185
180
|
knowledge_dependencies: {
|
|
186
181
|
type: "array",
|
|
182
|
+
default: [],
|
|
187
183
|
items: {
|
|
188
|
-
type: "string"
|
|
184
|
+
type: "string",
|
|
185
|
+
description: "Glob patterns for blueprint files this agent needs access to"
|
|
189
186
|
}
|
|
190
187
|
},
|
|
191
188
|
prompt_engine_requirements: {
|
|
@@ -205,6 +202,11 @@ var agent_record_schema_default = {
|
|
|
205
202
|
}
|
|
206
203
|
}
|
|
207
204
|
},
|
|
205
|
+
metadata: {
|
|
206
|
+
type: "object",
|
|
207
|
+
description: "Optional framework-specific or deployment-specific metadata for agent extensions.\nCommon use cases: framework identification (langchain, google-adk), deployment info (provider, image, region),\ncost tracking (cost_per_invocation, currency), tool capabilities, maintainer info.\nThis field does NOT affect agent execution - it is purely informational.\n",
|
|
208
|
+
additionalProperties: true
|
|
209
|
+
},
|
|
208
210
|
engine: {
|
|
209
211
|
type: "object",
|
|
210
212
|
oneOf: [
|
|
@@ -212,63 +214,365 @@ var agent_record_schema_default = {
|
|
|
212
214
|
required: [
|
|
213
215
|
"type"
|
|
214
216
|
],
|
|
217
|
+
additionalProperties: false,
|
|
215
218
|
properties: {
|
|
216
219
|
type: {
|
|
217
220
|
const: "local"
|
|
218
221
|
},
|
|
219
222
|
runtime: {
|
|
220
|
-
type: "string"
|
|
223
|
+
type: "string",
|
|
224
|
+
description: "Runtime environment (typescript, python, etc.)"
|
|
221
225
|
},
|
|
222
226
|
entrypoint: {
|
|
223
|
-
type: "string"
|
|
227
|
+
type: "string",
|
|
228
|
+
description: "Path to the agent entry file"
|
|
224
229
|
},
|
|
225
230
|
function: {
|
|
226
|
-
type: "string"
|
|
231
|
+
type: "string",
|
|
232
|
+
description: "Function name to invoke"
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
},
|
|
230
236
|
{
|
|
231
237
|
required: [
|
|
232
|
-
"type"
|
|
238
|
+
"type",
|
|
239
|
+
"url"
|
|
233
240
|
],
|
|
241
|
+
additionalProperties: false,
|
|
234
242
|
properties: {
|
|
235
243
|
type: {
|
|
236
244
|
const: "api"
|
|
237
245
|
},
|
|
238
246
|
url: {
|
|
239
|
-
type: "string"
|
|
247
|
+
type: "string",
|
|
248
|
+
format: "uri",
|
|
249
|
+
description: "HTTP endpoint for the agent"
|
|
240
250
|
},
|
|
241
251
|
method: {
|
|
242
252
|
type: "string",
|
|
243
253
|
enum: [
|
|
244
254
|
"POST",
|
|
245
255
|
"GET"
|
|
246
|
-
]
|
|
256
|
+
],
|
|
257
|
+
default: "POST"
|
|
247
258
|
},
|
|
248
259
|
auth: {
|
|
249
|
-
type: "object"
|
|
260
|
+
type: "object",
|
|
261
|
+
description: "Authentication configuration for API requests",
|
|
262
|
+
additionalProperties: true,
|
|
263
|
+
properties: {
|
|
264
|
+
type: {
|
|
265
|
+
type: "string",
|
|
266
|
+
enum: [
|
|
267
|
+
"bearer",
|
|
268
|
+
"oauth",
|
|
269
|
+
"api-key",
|
|
270
|
+
"actor-signature"
|
|
271
|
+
],
|
|
272
|
+
description: "Authentication type. 'actor-signature' uses the agent's ActorRecord keypair to sign requests."
|
|
273
|
+
},
|
|
274
|
+
secret_key: {
|
|
275
|
+
type: "string",
|
|
276
|
+
description: "Reference to secret in Secret Manager (for bearer/api-key/oauth auth types)"
|
|
277
|
+
},
|
|
278
|
+
token: {
|
|
279
|
+
type: "string",
|
|
280
|
+
description: "Direct token value (not recommended for production, use secret_key instead)"
|
|
281
|
+
}
|
|
282
|
+
}
|
|
250
283
|
}
|
|
251
284
|
}
|
|
252
285
|
},
|
|
253
286
|
{
|
|
254
287
|
required: [
|
|
255
|
-
"type"
|
|
288
|
+
"type",
|
|
289
|
+
"url"
|
|
256
290
|
],
|
|
291
|
+
additionalProperties: false,
|
|
257
292
|
properties: {
|
|
258
293
|
type: {
|
|
259
294
|
const: "mcp"
|
|
260
295
|
},
|
|
261
296
|
url: {
|
|
262
|
-
type: "string"
|
|
297
|
+
type: "string",
|
|
298
|
+
format: "uri",
|
|
299
|
+
description: "MCP server endpoint"
|
|
263
300
|
},
|
|
264
301
|
auth: {
|
|
265
|
-
type: "object"
|
|
302
|
+
type: "object",
|
|
303
|
+
description: "Authentication configuration for MCP server",
|
|
304
|
+
additionalProperties: true,
|
|
305
|
+
properties: {
|
|
306
|
+
type: {
|
|
307
|
+
type: "string",
|
|
308
|
+
enum: [
|
|
309
|
+
"bearer",
|
|
310
|
+
"oauth",
|
|
311
|
+
"api-key",
|
|
312
|
+
"actor-signature"
|
|
313
|
+
],
|
|
314
|
+
description: "Authentication type. 'actor-signature' uses the agent's ActorRecord keypair to sign requests."
|
|
315
|
+
},
|
|
316
|
+
secret_key: {
|
|
317
|
+
type: "string",
|
|
318
|
+
description: "Reference to secret in Secret Manager (for bearer/api-key/oauth auth types)"
|
|
319
|
+
},
|
|
320
|
+
token: {
|
|
321
|
+
type: "string",
|
|
322
|
+
description: "Direct token value (not recommended for production, use secret_key instead)"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
required: [
|
|
330
|
+
"type"
|
|
331
|
+
],
|
|
332
|
+
additionalProperties: false,
|
|
333
|
+
properties: {
|
|
334
|
+
type: {
|
|
335
|
+
const: "custom"
|
|
336
|
+
},
|
|
337
|
+
protocol: {
|
|
338
|
+
type: "string",
|
|
339
|
+
description: "Custom protocol identifier (e.g., 'a2a', 'grpc')"
|
|
340
|
+
},
|
|
341
|
+
config: {
|
|
342
|
+
type: "object",
|
|
343
|
+
description: "Protocol-specific configuration"
|
|
266
344
|
}
|
|
267
345
|
}
|
|
268
346
|
}
|
|
269
347
|
]
|
|
270
348
|
}
|
|
271
|
-
}
|
|
349
|
+
},
|
|
350
|
+
examples: [
|
|
351
|
+
{
|
|
352
|
+
id: "agent:scribe",
|
|
353
|
+
status: "active",
|
|
354
|
+
engine: {
|
|
355
|
+
type: "local",
|
|
356
|
+
runtime: "typescript",
|
|
357
|
+
entrypoint: "packages/agents/scribe/index.ts",
|
|
358
|
+
function: "runScribe"
|
|
359
|
+
},
|
|
360
|
+
metadata: {
|
|
361
|
+
purpose: "documentation-generation",
|
|
362
|
+
maintainer: "team:platform"
|
|
363
|
+
},
|
|
364
|
+
triggers: [
|
|
365
|
+
{
|
|
366
|
+
type: "manual"
|
|
367
|
+
}
|
|
368
|
+
],
|
|
369
|
+
knowledge_dependencies: [
|
|
370
|
+
"packages/blueprints/**/*.md"
|
|
371
|
+
]
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "agent:langchain-analyzer",
|
|
375
|
+
status: "active",
|
|
376
|
+
engine: {
|
|
377
|
+
type: "api",
|
|
378
|
+
url: "https://langchain-service-xyz.a.run.app/analyze",
|
|
379
|
+
method: "POST",
|
|
380
|
+
auth: {
|
|
381
|
+
type: "actor-signature"
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
metadata: {
|
|
385
|
+
framework: "langchain",
|
|
386
|
+
version: "0.2.0",
|
|
387
|
+
model: "gpt-4-turbo",
|
|
388
|
+
deployment: {
|
|
389
|
+
provider: "gcp",
|
|
390
|
+
service: "cloud-run",
|
|
391
|
+
region: "us-central1"
|
|
392
|
+
},
|
|
393
|
+
cost_per_invocation: 0.03,
|
|
394
|
+
currency: "USD"
|
|
395
|
+
},
|
|
396
|
+
triggers: [
|
|
397
|
+
{
|
|
398
|
+
type: "webhook",
|
|
399
|
+
event: "task.ready"
|
|
400
|
+
}
|
|
401
|
+
],
|
|
402
|
+
knowledge_dependencies: [
|
|
403
|
+
"docs/architecture/**/*.md"
|
|
404
|
+
]
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
id: "agent:sentiment-analyzer",
|
|
408
|
+
status: "active",
|
|
409
|
+
engine: {
|
|
410
|
+
type: "api",
|
|
411
|
+
url: "http://sentiment-analyzer:8082/analyze",
|
|
412
|
+
method: "POST",
|
|
413
|
+
auth: {
|
|
414
|
+
type: "actor-signature"
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
metadata: {
|
|
418
|
+
framework: "google-adk",
|
|
419
|
+
version: "1.0.0",
|
|
420
|
+
model: "gemini-pro",
|
|
421
|
+
deployment: {
|
|
422
|
+
runtime: "docker",
|
|
423
|
+
image: "gitgov/sentiment-analyzer:v1.2.0",
|
|
424
|
+
port: 8082
|
|
425
|
+
},
|
|
426
|
+
max_tokens: 1024
|
|
427
|
+
},
|
|
428
|
+
triggers: [
|
|
429
|
+
{
|
|
430
|
+
type: "webhook",
|
|
431
|
+
event: "feedback.created"
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
id: "agent:cursor-reviewer",
|
|
437
|
+
status: "active",
|
|
438
|
+
engine: {
|
|
439
|
+
type: "mcp",
|
|
440
|
+
url: "http://localhost:8083/mcp",
|
|
441
|
+
auth: {
|
|
442
|
+
type: "actor-signature"
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
metadata: {
|
|
446
|
+
ide: "cursor",
|
|
447
|
+
tool: "code-review",
|
|
448
|
+
accepts_tools: [
|
|
449
|
+
"review",
|
|
450
|
+
"refactor",
|
|
451
|
+
"test"
|
|
452
|
+
]
|
|
453
|
+
},
|
|
454
|
+
knowledge_dependencies: [
|
|
455
|
+
"packages/**/*.ts",
|
|
456
|
+
"packages/**/*.tsx"
|
|
457
|
+
],
|
|
458
|
+
triggers: [
|
|
459
|
+
{
|
|
460
|
+
type: "webhook",
|
|
461
|
+
event: "task.status.ready"
|
|
462
|
+
}
|
|
463
|
+
]
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
id: "agent:deepl-translator",
|
|
467
|
+
status: "active",
|
|
468
|
+
engine: {
|
|
469
|
+
type: "api",
|
|
470
|
+
url: "https://api.deepl.com/v2/translate",
|
|
471
|
+
method: "POST",
|
|
472
|
+
auth: {
|
|
473
|
+
type: "bearer",
|
|
474
|
+
secret_key: "DEEPL_API_KEY"
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
metadata: {
|
|
478
|
+
provider: "deepl",
|
|
479
|
+
supported_languages: [
|
|
480
|
+
"EN",
|
|
481
|
+
"ES",
|
|
482
|
+
"FR",
|
|
483
|
+
"DE",
|
|
484
|
+
"PT"
|
|
485
|
+
],
|
|
486
|
+
max_chars_per_request: 5e3
|
|
487
|
+
},
|
|
488
|
+
triggers: [
|
|
489
|
+
{
|
|
490
|
+
type: "manual"
|
|
491
|
+
}
|
|
492
|
+
]
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
id: "agent:coordinator",
|
|
496
|
+
status: "active",
|
|
497
|
+
engine: {
|
|
498
|
+
type: "custom",
|
|
499
|
+
protocol: "a2a",
|
|
500
|
+
config: {
|
|
501
|
+
endpoint: "https://agent-hub.gitgov.io/a2a",
|
|
502
|
+
version: "draft-2025-01",
|
|
503
|
+
capabilities: [
|
|
504
|
+
"task-delegation",
|
|
505
|
+
"status-sync",
|
|
506
|
+
"feedback-loop"
|
|
507
|
+
]
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
metadata: {
|
|
511
|
+
purpose: "multi-agent-orchestration",
|
|
512
|
+
experimental: true
|
|
513
|
+
},
|
|
514
|
+
triggers: [
|
|
515
|
+
{
|
|
516
|
+
type: "scheduled",
|
|
517
|
+
cron: "0 */4 * * *"
|
|
518
|
+
}
|
|
519
|
+
]
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
id: "agent:minimal-watcher",
|
|
523
|
+
engine: {
|
|
524
|
+
type: "local"
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
id: "agent:local-mcp-server",
|
|
529
|
+
status: "active",
|
|
530
|
+
engine: {
|
|
531
|
+
type: "mcp",
|
|
532
|
+
url: "http://localhost:9000/mcp"
|
|
533
|
+
},
|
|
534
|
+
knowledge_dependencies: [
|
|
535
|
+
"packages/blueprints/**/*.md"
|
|
536
|
+
],
|
|
537
|
+
triggers: [
|
|
538
|
+
{
|
|
539
|
+
type: "manual"
|
|
540
|
+
}
|
|
541
|
+
]
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
id: "agent:code-reviewer",
|
|
545
|
+
status: "active",
|
|
546
|
+
engine: {
|
|
547
|
+
type: "local",
|
|
548
|
+
runtime: "typescript",
|
|
549
|
+
entrypoint: "packages/agents/code-reviewer/index.ts",
|
|
550
|
+
function: "reviewCode"
|
|
551
|
+
},
|
|
552
|
+
prompt_engine_requirements: {
|
|
553
|
+
roles: [
|
|
554
|
+
"code-reviewer",
|
|
555
|
+
"security-auditor"
|
|
556
|
+
],
|
|
557
|
+
skills: [
|
|
558
|
+
"typescript",
|
|
559
|
+
"security-best-practices",
|
|
560
|
+
"code-quality-analysis"
|
|
561
|
+
]
|
|
562
|
+
},
|
|
563
|
+
knowledge_dependencies: [
|
|
564
|
+
"packages/**/*.ts",
|
|
565
|
+
"packages/**/*.tsx"
|
|
566
|
+
],
|
|
567
|
+
triggers: [
|
|
568
|
+
{
|
|
569
|
+
type: "webhook",
|
|
570
|
+
event: "pull-request.opened",
|
|
571
|
+
filter: "branch:main"
|
|
572
|
+
}
|
|
573
|
+
]
|
|
574
|
+
}
|
|
575
|
+
]
|
|
272
576
|
};
|
|
273
577
|
|
|
274
578
|
// src/schemas/generated/changelog_record_schema.json
|
|
@@ -276,226 +580,178 @@ var changelog_record_schema_default = {
|
|
|
276
580
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
277
581
|
$id: "changelog_record_schema.json",
|
|
278
582
|
title: "ChangelogRecord",
|
|
279
|
-
description: "Canonical schema for changelog records -
|
|
583
|
+
description: "Canonical schema for changelog records - aggregates N tasks into 1 release note",
|
|
280
584
|
additionalProperties: false,
|
|
281
585
|
type: "object",
|
|
282
586
|
required: [
|
|
283
587
|
"id",
|
|
284
|
-
"entityType",
|
|
285
|
-
"entityId",
|
|
286
|
-
"changeType",
|
|
287
588
|
"title",
|
|
288
589
|
"description",
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"triggeredBy",
|
|
292
|
-
"reason",
|
|
293
|
-
"riskLevel"
|
|
590
|
+
"relatedTasks",
|
|
591
|
+
"completedAt"
|
|
294
592
|
],
|
|
295
593
|
properties: {
|
|
296
594
|
id: {
|
|
297
595
|
type: "string",
|
|
298
|
-
pattern: "^\\d{10}-changelog-[a-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
"cycle",
|
|
306
|
-
"agent",
|
|
307
|
-
"system",
|
|
308
|
-
"configuration"
|
|
309
|
-
],
|
|
310
|
-
description: "Type of the primary entity that changed"
|
|
311
|
-
},
|
|
312
|
-
entityId: {
|
|
313
|
-
type: "string",
|
|
314
|
-
description: "ID of the primary entity that changed"
|
|
315
|
-
},
|
|
316
|
-
changeType: {
|
|
317
|
-
type: "string",
|
|
318
|
-
enum: [
|
|
319
|
-
"creation",
|
|
320
|
-
"completion",
|
|
321
|
-
"update",
|
|
322
|
-
"deletion",
|
|
323
|
-
"hotfix"
|
|
324
|
-
],
|
|
325
|
-
description: "The nature of the change"
|
|
596
|
+
pattern: "^\\d{10}-changelog-[a-z0-9-]{1,50}$",
|
|
597
|
+
maxLength: 71,
|
|
598
|
+
description: "Unique identifier for the changelog entry",
|
|
599
|
+
examples: [
|
|
600
|
+
"1752707800-changelog-sistema-autenticacion-v1",
|
|
601
|
+
"1752707800-changelog-sprint-24-api-performance"
|
|
602
|
+
]
|
|
326
603
|
},
|
|
327
604
|
title: {
|
|
328
605
|
type: "string",
|
|
329
606
|
minLength: 10,
|
|
330
607
|
maxLength: 150,
|
|
331
|
-
description: "Executive title of the
|
|
608
|
+
description: "Executive title of the deliverable",
|
|
609
|
+
examples: [
|
|
610
|
+
"Sistema de Autenticaci\xF3n Completo v1.0",
|
|
611
|
+
"Sprint 24 - Performance Optimizations"
|
|
612
|
+
]
|
|
332
613
|
},
|
|
333
614
|
description: {
|
|
334
615
|
type: "string",
|
|
335
616
|
minLength: 20,
|
|
336
617
|
maxLength: 5e3,
|
|
337
|
-
description: "Detailed description of the
|
|
618
|
+
description: "Detailed description of the value delivered, including key decisions and impact"
|
|
338
619
|
},
|
|
339
|
-
|
|
620
|
+
relatedTasks: {
|
|
621
|
+
type: "array",
|
|
622
|
+
items: {
|
|
623
|
+
type: "string",
|
|
624
|
+
pattern: "^\\d{10}-task-[a-z0-9-]{1,50}$"
|
|
625
|
+
},
|
|
626
|
+
minItems: 1,
|
|
627
|
+
description: "IDs of tasks that compose this deliverable (minimum 1 required)"
|
|
628
|
+
},
|
|
629
|
+
completedAt: {
|
|
340
630
|
type: "number",
|
|
341
631
|
minimum: 0,
|
|
342
|
-
description: "Unix timestamp in seconds when the
|
|
343
|
-
},
|
|
344
|
-
trigger: {
|
|
345
|
-
type: "string",
|
|
346
|
-
enum: [
|
|
347
|
-
"manual",
|
|
348
|
-
"automated",
|
|
349
|
-
"emergency"
|
|
350
|
-
],
|
|
351
|
-
description: "How the change was initiated"
|
|
632
|
+
description: "Unix timestamp in seconds when the deliverable was completed"
|
|
352
633
|
},
|
|
353
|
-
|
|
354
|
-
type: "
|
|
355
|
-
|
|
634
|
+
relatedCycles: {
|
|
635
|
+
type: "array",
|
|
636
|
+
items: {
|
|
637
|
+
type: "string",
|
|
638
|
+
pattern: "^\\d{10}-cycle-[a-z0-9-]{1,50}$"
|
|
639
|
+
},
|
|
640
|
+
default: [],
|
|
641
|
+
description: "Optional IDs of cycles related to this deliverable"
|
|
356
642
|
},
|
|
357
|
-
|
|
358
|
-
type: "
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
643
|
+
relatedExecutions: {
|
|
644
|
+
type: "array",
|
|
645
|
+
items: {
|
|
646
|
+
type: "string",
|
|
647
|
+
pattern: "^\\d{10}-exec-[a-z0-9-]{1,50}$"
|
|
648
|
+
},
|
|
649
|
+
default: [],
|
|
650
|
+
description: "Optional IDs of key execution records related to this work"
|
|
362
651
|
},
|
|
363
|
-
|
|
652
|
+
version: {
|
|
364
653
|
type: "string",
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
"
|
|
370
|
-
|
|
371
|
-
|
|
654
|
+
minLength: 1,
|
|
655
|
+
maxLength: 50,
|
|
656
|
+
description: "Optional version or release identifier (e.g., 'v1.0.0', 'sprint-24')",
|
|
657
|
+
examples: [
|
|
658
|
+
"v1.0.0",
|
|
659
|
+
"v2.1.3",
|
|
660
|
+
"sprint-24"
|
|
661
|
+
]
|
|
372
662
|
},
|
|
373
|
-
|
|
663
|
+
tags: {
|
|
374
664
|
type: "array",
|
|
375
665
|
items: {
|
|
376
|
-
type: "string"
|
|
666
|
+
type: "string",
|
|
667
|
+
pattern: "^[a-z0-9-]+(:[a-z0-9-]+)*$"
|
|
377
668
|
},
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
usersAffected: {
|
|
381
|
-
type: "number",
|
|
382
|
-
minimum: 0,
|
|
383
|
-
description: "Number of users impacted by this change"
|
|
384
|
-
},
|
|
385
|
-
downtime: {
|
|
386
|
-
type: "number",
|
|
387
|
-
minimum: 0,
|
|
388
|
-
description: "Downtime in seconds caused by this change"
|
|
669
|
+
default: [],
|
|
670
|
+
description: "Optional tags for categorization (e.g., 'feature:auth', 'bugfix', 'security')"
|
|
389
671
|
},
|
|
390
|
-
|
|
672
|
+
commits: {
|
|
391
673
|
type: "array",
|
|
392
674
|
items: {
|
|
393
|
-
type: "string"
|
|
675
|
+
type: "string",
|
|
676
|
+
maxLength: 100
|
|
394
677
|
},
|
|
395
|
-
|
|
678
|
+
default: [],
|
|
679
|
+
description: "Optional list of git commit hashes related to this deliverable"
|
|
396
680
|
},
|
|
397
|
-
|
|
681
|
+
files: {
|
|
398
682
|
type: "array",
|
|
399
683
|
items: {
|
|
400
|
-
type: "string"
|
|
684
|
+
type: "string",
|
|
685
|
+
maxLength: 500
|
|
401
686
|
},
|
|
402
|
-
|
|
687
|
+
default: [],
|
|
688
|
+
description: "Optional list of main files that were created or modified"
|
|
403
689
|
},
|
|
404
|
-
|
|
690
|
+
notes: {
|
|
405
691
|
type: "string",
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
description: "Step-by-step instructions to rollback this change"
|
|
409
|
-
},
|
|
410
|
-
references: {
|
|
411
|
-
type: "object",
|
|
412
|
-
additionalProperties: false,
|
|
413
|
-
properties: {
|
|
414
|
-
tasks: {
|
|
415
|
-
type: "array",
|
|
416
|
-
items: {
|
|
417
|
-
type: "string"
|
|
418
|
-
},
|
|
419
|
-
description: "IDs of related TaskRecords"
|
|
420
|
-
},
|
|
421
|
-
cycles: {
|
|
422
|
-
type: "array",
|
|
423
|
-
items: {
|
|
424
|
-
type: "string"
|
|
425
|
-
},
|
|
426
|
-
description: "IDs of related CycleRecords"
|
|
427
|
-
},
|
|
428
|
-
executions: {
|
|
429
|
-
type: "array",
|
|
430
|
-
items: {
|
|
431
|
-
type: "string"
|
|
432
|
-
},
|
|
433
|
-
description: "IDs of related ExecutionRecords"
|
|
434
|
-
},
|
|
435
|
-
changelogs: {
|
|
436
|
-
type: "array",
|
|
437
|
-
items: {
|
|
438
|
-
type: "string"
|
|
439
|
-
},
|
|
440
|
-
description: "IDs of related ChangelogRecords"
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
description: "Cross-references to other GitGovernance records"
|
|
692
|
+
maxLength: 3e3,
|
|
693
|
+
description: "Optional additional context, decisions, or learnings"
|
|
444
694
|
}
|
|
445
695
|
},
|
|
446
696
|
examples: [
|
|
447
697
|
{
|
|
448
|
-
id: "1752707800-changelog-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
"
|
|
461
|
-
|
|
698
|
+
id: "1752707800-changelog-sistema-autenticacion-v1",
|
|
699
|
+
title: "Sistema de Autenticaci\xF3n Completo v1.0",
|
|
700
|
+
description: "Implementaci\xF3n completa del sistema de autenticaci\xF3n con OAuth2, 2FA via TOTP, recuperaci\xF3n de contrase\xF1a, y UI responsive. Incluye tests E2E completos (95% coverage) y documentaci\xF3n t\xE9cnica actualizada.",
|
|
701
|
+
relatedTasks: [
|
|
702
|
+
"1752274500-task-crear-ui-login",
|
|
703
|
+
"1752274600-task-integrar-oauth2-backend",
|
|
704
|
+
"1752274700-task-implementar-2fa-totp",
|
|
705
|
+
"1752274800-task-tests-e2e-auth",
|
|
706
|
+
"1752274900-task-documentar-flujo-auth"
|
|
707
|
+
],
|
|
708
|
+
completedAt: 1752707800,
|
|
709
|
+
relatedCycles: [
|
|
710
|
+
"1752200000-cycle-q1-auth-milestone"
|
|
711
|
+
],
|
|
712
|
+
relatedExecutions: [
|
|
713
|
+
"1752274550-exec-analisis-auth-providers",
|
|
714
|
+
"1752707750-exec-final-integration-test"
|
|
715
|
+
],
|
|
716
|
+
version: "v1.0.0",
|
|
717
|
+
tags: [
|
|
718
|
+
"feature:auth",
|
|
719
|
+
"security",
|
|
720
|
+
"frontend",
|
|
721
|
+
"backend"
|
|
462
722
|
],
|
|
463
723
|
commits: [
|
|
464
724
|
"abc123def",
|
|
465
|
-
"456ghi789"
|
|
725
|
+
"456ghi789",
|
|
726
|
+
"jkl012mno"
|
|
466
727
|
],
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
728
|
+
files: [
|
|
729
|
+
"src/pages/Login.tsx",
|
|
730
|
+
"src/services/auth.ts",
|
|
731
|
+
"src/components/TwoFactorSetup.tsx",
|
|
732
|
+
"e2e/auth.spec.ts"
|
|
733
|
+
],
|
|
734
|
+
notes: "Decisi\xF3n t\xE9cnica: Usamos NextAuth.js despu\xE9s de evaluar Passport.js. El 2FA se implement\xF3 con TOTP (Google Authenticator compatible) en lugar de SMS por seguridad y costo."
|
|
475
735
|
},
|
|
476
736
|
{
|
|
477
|
-
id: "1752707900-changelog-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
"payment
|
|
490
|
-
"order-service",
|
|
491
|
-
"notification-service"
|
|
737
|
+
id: "1752707900-changelog-hotfix-payment-timeout",
|
|
738
|
+
title: "Hotfix: Critical Payment Timeout Fix",
|
|
739
|
+
description: "Fixed critical payment timeout issue affecting 15% of transactions. Increased timeout from 5s to 30s and added circuit breaker pattern for third-party API calls.",
|
|
740
|
+
relatedTasks: [
|
|
741
|
+
"1752707850-task-fix-payment-timeout",
|
|
742
|
+
"1752707870-task-add-circuit-breaker"
|
|
743
|
+
],
|
|
744
|
+
completedAt: 1752707900,
|
|
745
|
+
version: "v1.2.1",
|
|
746
|
+
tags: [
|
|
747
|
+
"hotfix",
|
|
748
|
+
"critical",
|
|
749
|
+
"payment"
|
|
492
750
|
],
|
|
493
|
-
usersAffected: 25e3,
|
|
494
|
-
downtime: 0,
|
|
495
|
-
rollbackInstructions: "1. Revert to payment-gateway:v2.1.4\n2. kubectl rollout restart deployment/payment-gateway\n3. Monitor success rate for 10 minutes",
|
|
496
751
|
commits: [
|
|
497
|
-
"
|
|
498
|
-
]
|
|
752
|
+
"xyz789abc"
|
|
753
|
+
],
|
|
754
|
+
notes: "Emergency response to production incident. Deployed to production within 2 hours."
|
|
499
755
|
}
|
|
500
756
|
]
|
|
501
757
|
};
|
|
@@ -505,7 +761,7 @@ var cycle_record_schema_default = {
|
|
|
505
761
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
506
762
|
$id: "cycle_record_schema.json",
|
|
507
763
|
title: "CycleRecord",
|
|
508
|
-
description: "Canonical schema for cycle records
|
|
764
|
+
description: "Canonical schema for cycle records - strategic grouping of work",
|
|
509
765
|
additionalProperties: false,
|
|
510
766
|
type: "object",
|
|
511
767
|
required: [
|
|
@@ -517,13 +773,24 @@ var cycle_record_schema_default = {
|
|
|
517
773
|
id: {
|
|
518
774
|
type: "string",
|
|
519
775
|
pattern: "^\\d{10}-cycle-[a-z0-9-]{1,50}$",
|
|
520
|
-
|
|
776
|
+
maxLength: 67,
|
|
777
|
+
description: "Unique identifier for the cycle (10 timestamp + 1 dash + 5 'cycle' + 1 dash + max 50 slug = 67 max)",
|
|
778
|
+
examples: [
|
|
779
|
+
"1754400000-cycle-sprint-24-api-performance",
|
|
780
|
+
"1754500000-cycle-auth-system-v2",
|
|
781
|
+
"1754600000-cycle-q4-2025-growth"
|
|
782
|
+
]
|
|
521
783
|
},
|
|
522
784
|
title: {
|
|
523
785
|
type: "string",
|
|
524
786
|
minLength: 1,
|
|
525
787
|
maxLength: 256,
|
|
526
|
-
description: "Human-readable title for the cycle (e.g., 'Sprint 24.
|
|
788
|
+
description: "Human-readable title for the cycle (e.g., 'Sprint 24', 'Auth v2.0', 'Q4 2025')",
|
|
789
|
+
examples: [
|
|
790
|
+
"Sprint 24 - API Performance",
|
|
791
|
+
"Authentication System v2.0",
|
|
792
|
+
"Q4 2025 - Growth & Scale"
|
|
793
|
+
]
|
|
527
794
|
},
|
|
528
795
|
status: {
|
|
529
796
|
type: "string",
|
|
@@ -539,45 +806,114 @@ var cycle_record_schema_default = {
|
|
|
539
806
|
type: "array",
|
|
540
807
|
items: {
|
|
541
808
|
type: "string",
|
|
542
|
-
pattern: "^\\d{10}-task-[a-z0-9-]{1,50}$"
|
|
543
|
-
|
|
809
|
+
pattern: "^\\d{10}-task-[a-z0-9-]{1,50}$",
|
|
810
|
+
maxLength: 66
|
|
811
|
+
},
|
|
812
|
+
default: [],
|
|
813
|
+
description: "Optional array of Task IDs that belong to this cycle. Can be empty for cycles that only contain child cycles. (10 timestamp + 1 dash + 4 'task' + 1 dash + max 50 slug = 66 max)",
|
|
814
|
+
examples: [
|
|
815
|
+
[
|
|
816
|
+
"1752274500-task-optimizar-endpoint-search",
|
|
817
|
+
"1752360900-task-anadir-cache-a-redis"
|
|
818
|
+
]
|
|
819
|
+
]
|
|
544
820
|
},
|
|
545
821
|
childCycleIds: {
|
|
546
822
|
type: "array",
|
|
547
823
|
items: {
|
|
548
824
|
type: "string",
|
|
549
|
-
pattern: "^\\d{10}-cycle-[a-z0-9-]{1,50}$"
|
|
825
|
+
pattern: "^\\d{10}-cycle-[a-z0-9-]{1,50}$",
|
|
826
|
+
maxLength: 67
|
|
550
827
|
},
|
|
551
|
-
|
|
828
|
+
default: [],
|
|
829
|
+
description: "Optional array of Cycle IDs that are children of this cycle, allowing for hierarchies (e.g., Q1 containing Sprint 1, Sprint 2, Sprint 3). (10 timestamp + 1 dash + 5 'cycle' + 1 dash + max 50 slug = 67 max)",
|
|
830
|
+
examples: [
|
|
831
|
+
[
|
|
832
|
+
"1754400000-cycle-sprint-24",
|
|
833
|
+
"1754500000-cycle-sprint-25"
|
|
834
|
+
]
|
|
835
|
+
]
|
|
552
836
|
},
|
|
553
837
|
tags: {
|
|
554
838
|
type: "array",
|
|
555
839
|
items: {
|
|
556
840
|
type: "string",
|
|
557
|
-
pattern: "^[a-z0-9-]+(:[a-z0-9-]+)*$"
|
|
841
|
+
pattern: "^[a-z0-9-]+(:[a-z0-9-]+)*$",
|
|
842
|
+
maxLength: 100
|
|
558
843
|
},
|
|
559
844
|
default: [],
|
|
560
|
-
description: "Optional list of key:value tags for categorization (e.g., 'roadmap:q4', 'team:alpha')."
|
|
845
|
+
description: "Optional list of key:value tags for categorization (e.g., 'roadmap:q4', 'team:alpha', 'okr:growth').",
|
|
846
|
+
examples: [
|
|
847
|
+
[
|
|
848
|
+
"roadmap:q4",
|
|
849
|
+
"team:backend"
|
|
850
|
+
],
|
|
851
|
+
[
|
|
852
|
+
"sprint:24",
|
|
853
|
+
"focus:performance"
|
|
854
|
+
],
|
|
855
|
+
[
|
|
856
|
+
"milestone:v2",
|
|
857
|
+
"security"
|
|
858
|
+
]
|
|
859
|
+
]
|
|
561
860
|
},
|
|
562
861
|
notes: {
|
|
563
862
|
type: "string",
|
|
863
|
+
minLength: 0,
|
|
564
864
|
maxLength: 1e4,
|
|
565
|
-
description: "
|
|
865
|
+
description: "Optional description of the cycle's goals, objectives, and context"
|
|
566
866
|
}
|
|
567
867
|
},
|
|
568
868
|
examples: [
|
|
569
869
|
{
|
|
570
|
-
id: "1754400000-cycle-sprint-
|
|
571
|
-
title: "Sprint
|
|
870
|
+
id: "1754400000-cycle-sprint-24-api-performance",
|
|
871
|
+
title: "Sprint 24 - API Performance",
|
|
572
872
|
status: "active",
|
|
573
873
|
taskIds: [
|
|
574
874
|
"1752274500-task-optimizar-endpoint-search",
|
|
575
|
-
"1752360900-task-anadir-cache-a-redis"
|
|
875
|
+
"1752360900-task-anadir-cache-a-redis",
|
|
876
|
+
"1752447300-task-implementar-rate-limiting"
|
|
576
877
|
],
|
|
577
878
|
tags: [
|
|
578
|
-
"
|
|
879
|
+
"sprint:24",
|
|
880
|
+
"team:backend",
|
|
881
|
+
"focus:performance"
|
|
579
882
|
],
|
|
580
|
-
notes: "Objetivo: Reducir la latencia p95 de la API por debajo de 200ms."
|
|
883
|
+
notes: "Objetivo: Reducir la latencia p95 de la API por debajo de 200ms y preparar infraestructura para Black Friday."
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
id: "1754500000-cycle-auth-system-v2",
|
|
887
|
+
title: "Authentication System v2.0",
|
|
888
|
+
status: "planning",
|
|
889
|
+
taskIds: [
|
|
890
|
+
"1752274500-task-oauth2-integration",
|
|
891
|
+
"1752360900-task-2fa-implementation",
|
|
892
|
+
"1752447300-task-password-recovery",
|
|
893
|
+
"1752533700-task-session-management"
|
|
894
|
+
],
|
|
895
|
+
tags: [
|
|
896
|
+
"milestone:v2",
|
|
897
|
+
"security",
|
|
898
|
+
"feature:auth"
|
|
899
|
+
],
|
|
900
|
+
notes: "Milestone mayor: Sistema completo de autenticaci\xF3n con OAuth2, 2FA, y gesti\xF3n avanzada de sesiones. Cr\xEDtico para lanzamiento Q4."
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
id: "1754600000-cycle-q4-2025-growth",
|
|
904
|
+
title: "Q4 2025 - Growth & Scale",
|
|
905
|
+
status: "active",
|
|
906
|
+
childCycleIds: [
|
|
907
|
+
"1754400000-cycle-sprint-24-api-performance",
|
|
908
|
+
"1754500000-cycle-auth-system-v2",
|
|
909
|
+
"1754650000-cycle-mobile-app-launch"
|
|
910
|
+
],
|
|
911
|
+
tags: [
|
|
912
|
+
"roadmap:q4",
|
|
913
|
+
"strategy:growth",
|
|
914
|
+
"okr:scale-to-1m-users"
|
|
915
|
+
],
|
|
916
|
+
notes: "Objetivo trimestral: Escalar a 1M usuarios activos. Incluye mejoras de performance, nuevo sistema de auth, y lanzamiento de app m\xF3vil."
|
|
581
917
|
}
|
|
582
918
|
]
|
|
583
919
|
};
|
|
@@ -636,40 +972,42 @@ var embedded_metadata_schema_default = {
|
|
|
636
972
|
properties: {
|
|
637
973
|
keyId: {
|
|
638
974
|
type: "string",
|
|
639
|
-
|
|
975
|
+
pattern: "^(human|agent)(:[a-z0-9-]+)+$",
|
|
976
|
+
description: "The Actor ID of the signer (must match ActorRecord.id pattern)."
|
|
640
977
|
},
|
|
641
978
|
role: {
|
|
642
979
|
type: "string",
|
|
643
|
-
|
|
980
|
+
pattern: "^([a-z-]+|custom:[a-z0-9-]+)$",
|
|
981
|
+
minLength: 1,
|
|
982
|
+
maxLength: 50,
|
|
983
|
+
description: "The context role of the signature (e.g., 'author', 'reviewer', 'auditor', or 'custom:*')."
|
|
984
|
+
},
|
|
985
|
+
notes: {
|
|
986
|
+
type: "string",
|
|
987
|
+
minLength: 1,
|
|
988
|
+
maxLength: 1e3,
|
|
989
|
+
description: "Human-readable note from the signer. Part of the signature digest."
|
|
644
990
|
},
|
|
645
991
|
signature: {
|
|
646
992
|
type: "string",
|
|
647
|
-
|
|
993
|
+
pattern: "^[A-Za-z0-9+/]{86}==$",
|
|
994
|
+
description: "The Ed25519 signature (base64 encoded, 88 chars with padding) of the signature digest."
|
|
648
995
|
},
|
|
649
996
|
timestamp: {
|
|
650
997
|
type: "integer",
|
|
651
998
|
description: "Unix timestamp of the signature."
|
|
652
|
-
},
|
|
653
|
-
timestamp_iso: {
|
|
654
|
-
type: "string",
|
|
655
|
-
description: "ISO 8601 timestamp of the signature."
|
|
656
999
|
}
|
|
657
1000
|
},
|
|
658
1001
|
required: [
|
|
659
1002
|
"keyId",
|
|
660
1003
|
"role",
|
|
1004
|
+
"notes",
|
|
661
1005
|
"signature",
|
|
662
|
-
"timestamp"
|
|
663
|
-
|
|
664
|
-
|
|
1006
|
+
"timestamp"
|
|
1007
|
+
],
|
|
1008
|
+
additionalProperties: false
|
|
665
1009
|
},
|
|
666
1010
|
description: "An array of one or more signature objects."
|
|
667
|
-
},
|
|
668
|
-
audit: {
|
|
669
|
-
type: "string",
|
|
670
|
-
minLength: 1,
|
|
671
|
-
maxLength: 3e3,
|
|
672
|
-
description: "A human-readable audit stamp (e.g., from gitgov audit)."
|
|
673
1011
|
}
|
|
674
1012
|
},
|
|
675
1013
|
required: [
|
|
@@ -869,33 +1207,95 @@ var embedded_metadata_schema_default = {
|
|
|
869
1207
|
}
|
|
870
1208
|
}
|
|
871
1209
|
},
|
|
872
|
-
else: false
|
|
873
|
-
}
|
|
874
|
-
],
|
|
875
|
-
examples: [
|
|
1210
|
+
else: false
|
|
1211
|
+
}
|
|
1212
|
+
],
|
|
1213
|
+
examples: [
|
|
1214
|
+
{
|
|
1215
|
+
header: {
|
|
1216
|
+
version: "1.0",
|
|
1217
|
+
type: "task",
|
|
1218
|
+
payloadChecksum: "a1b2c3d4e5f6...",
|
|
1219
|
+
signatures: [
|
|
1220
|
+
{
|
|
1221
|
+
keyId: "human:lead-dev",
|
|
1222
|
+
role: "author",
|
|
1223
|
+
notes: "Initial task creation for OAuth 2.0 implementation",
|
|
1224
|
+
signature: "...",
|
|
1225
|
+
timestamp: 1752274500
|
|
1226
|
+
}
|
|
1227
|
+
]
|
|
1228
|
+
},
|
|
1229
|
+
payload: {
|
|
1230
|
+
id: "1752274500-task-implementar-auth",
|
|
1231
|
+
status: "pending",
|
|
1232
|
+
priority: "high",
|
|
1233
|
+
description: "Implementar autenticaci\xF3n OAuth 2.0.",
|
|
1234
|
+
tags: [
|
|
1235
|
+
"skill:go",
|
|
1236
|
+
"area:backend"
|
|
1237
|
+
]
|
|
1238
|
+
}
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
header: {
|
|
1242
|
+
version: "1.0",
|
|
1243
|
+
type: "execution",
|
|
1244
|
+
payloadChecksum: "b2c3d4e5f6a1...",
|
|
1245
|
+
signatures: [
|
|
1246
|
+
{
|
|
1247
|
+
keyId: "agent:cursor",
|
|
1248
|
+
role: "author",
|
|
1249
|
+
notes: "OAuth 2.0 flow completed with GitHub provider integration",
|
|
1250
|
+
signature: "...",
|
|
1251
|
+
timestamp: 1752274600
|
|
1252
|
+
},
|
|
1253
|
+
{
|
|
1254
|
+
keyId: "human:camilo",
|
|
1255
|
+
role: "reviewer",
|
|
1256
|
+
notes: "Reviewed and tested locally. LGTM.",
|
|
1257
|
+
signature: "...",
|
|
1258
|
+
timestamp: 1752274650
|
|
1259
|
+
}
|
|
1260
|
+
]
|
|
1261
|
+
},
|
|
1262
|
+
payload: {
|
|
1263
|
+
id: "1752274600-exec-implement-oauth",
|
|
1264
|
+
taskId: "1752274500-task-implement-oauth",
|
|
1265
|
+
type: "progress",
|
|
1266
|
+
title: "OAuth 2.0 flow implemented",
|
|
1267
|
+
result: "Completed the OAuth 2.0 authentication flow..."
|
|
1268
|
+
}
|
|
1269
|
+
},
|
|
876
1270
|
{
|
|
877
1271
|
header: {
|
|
878
1272
|
version: "1.0",
|
|
879
|
-
type: "
|
|
880
|
-
payloadChecksum: "
|
|
1273
|
+
type: "actor",
|
|
1274
|
+
payloadChecksum: "c3d4e5f6a1b2...",
|
|
881
1275
|
signatures: [
|
|
882
1276
|
{
|
|
883
|
-
keyId: "human:
|
|
1277
|
+
keyId: "human:admin",
|
|
884
1278
|
role: "author",
|
|
1279
|
+
notes: "New developer onboarded to team",
|
|
1280
|
+
signature: "...",
|
|
1281
|
+
timestamp: 1752274700
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
keyId: "agent:aion",
|
|
1285
|
+
role: "auditor",
|
|
1286
|
+
notes: "Actor verification: 10/10. Credentials validated.",
|
|
885
1287
|
signature: "...",
|
|
886
|
-
timestamp:
|
|
887
|
-
timestamp_iso: "2025-07-25T14:30:00Z"
|
|
1288
|
+
timestamp: 1752274705
|
|
888
1289
|
}
|
|
889
1290
|
]
|
|
890
1291
|
},
|
|
891
1292
|
payload: {
|
|
892
|
-
id: "
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
"
|
|
898
|
-
"area:backend"
|
|
1293
|
+
id: "human:new-developer",
|
|
1294
|
+
type: "human",
|
|
1295
|
+
displayName: "New Developer",
|
|
1296
|
+
publicKey: "...",
|
|
1297
|
+
roles: [
|
|
1298
|
+
"developer"
|
|
899
1299
|
]
|
|
900
1300
|
}
|
|
901
1301
|
}
|
|
@@ -907,29 +1307,32 @@ var execution_record_schema_default = {
|
|
|
907
1307
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
908
1308
|
$id: "execution_record_schema.json",
|
|
909
1309
|
title: "ExecutionRecord",
|
|
910
|
-
description: "Canonical schema for execution log records",
|
|
1310
|
+
description: "Canonical schema for execution log records - the universal event stream",
|
|
911
1311
|
additionalProperties: false,
|
|
912
1312
|
type: "object",
|
|
913
1313
|
required: [
|
|
914
1314
|
"id",
|
|
915
1315
|
"taskId",
|
|
1316
|
+
"type",
|
|
1317
|
+
"title",
|
|
916
1318
|
"result"
|
|
917
1319
|
],
|
|
918
1320
|
properties: {
|
|
919
1321
|
id: {
|
|
920
1322
|
type: "string",
|
|
921
1323
|
pattern: "^\\d{10}-exec-[a-z0-9-]{1,50}$",
|
|
922
|
-
maxLength:
|
|
923
|
-
description: "Unique identifier for the execution log entry",
|
|
1324
|
+
maxLength: 66,
|
|
1325
|
+
description: "Unique identifier for the execution log entry (10 timestamp + 1 dash + 4 'exec' + 1 dash + max 50 slug = 66 max)",
|
|
924
1326
|
examples: [
|
|
925
|
-
"1752275000-exec-
|
|
1327
|
+
"1752275000-exec-refactor-queries",
|
|
1328
|
+
"1752361200-exec-api-externa-caida"
|
|
926
1329
|
]
|
|
927
1330
|
},
|
|
928
1331
|
taskId: {
|
|
929
1332
|
type: "string",
|
|
930
1333
|
pattern: "^\\d{10}-task-[a-z0-9-]{1,50}$",
|
|
931
|
-
maxLength:
|
|
932
|
-
description: "ID of the parent task"
|
|
1334
|
+
maxLength: 66,
|
|
1335
|
+
description: "ID of the parent task this execution belongs to (10 timestamp + 1 dash + 4 'task' + 1 dash + max 50 slug = 66 max)"
|
|
933
1336
|
},
|
|
934
1337
|
type: {
|
|
935
1338
|
type: "string",
|
|
@@ -951,23 +1354,25 @@ var execution_record_schema_default = {
|
|
|
951
1354
|
},
|
|
952
1355
|
title: {
|
|
953
1356
|
type: "string",
|
|
1357
|
+
minLength: 1,
|
|
954
1358
|
maxLength: 256,
|
|
955
|
-
description: "Human-readable title for the execution",
|
|
1359
|
+
description: "Human-readable title for the execution (used to generate ID)",
|
|
956
1360
|
examples: [
|
|
957
|
-
"
|
|
958
|
-
"
|
|
1361
|
+
"Refactor de queries N+1",
|
|
1362
|
+
"API Externa Ca\xEDda",
|
|
1363
|
+
"Plan de implementaci\xF3n OAuth2"
|
|
959
1364
|
]
|
|
960
1365
|
},
|
|
961
1366
|
result: {
|
|
962
1367
|
type: "string",
|
|
963
1368
|
minLength: 10,
|
|
964
1369
|
maxLength: 22e3,
|
|
965
|
-
description:
|
|
1370
|
+
description: 'The tangible, verifiable output or result of the execution. \nThis is the "WHAT" - evidence of work or event summary.\n'
|
|
966
1371
|
},
|
|
967
1372
|
notes: {
|
|
968
1373
|
type: "string",
|
|
969
1374
|
maxLength: 6500,
|
|
970
|
-
description:
|
|
1375
|
+
description: 'Optional narrative, context and decisions behind the execution.\nThis is the "HOW" and "WHY" - the story behind the result.\n'
|
|
971
1376
|
},
|
|
972
1377
|
references: {
|
|
973
1378
|
type: "array",
|
|
@@ -975,19 +1380,79 @@ var execution_record_schema_default = {
|
|
|
975
1380
|
type: "string",
|
|
976
1381
|
maxLength: 500
|
|
977
1382
|
},
|
|
978
|
-
|
|
1383
|
+
default: [],
|
|
1384
|
+
description: "Optional list of typed references to relevant commits, files, PRs, or external documents.\nShould use typed prefixes for clarity and trazabilidad (see execution_protocol_appendix.md):\n- commit: Git commit SHA\n- pr: Pull Request number\n- file: File path (relative to repo root)\n- url: External URL\n- issue: GitHub Issue number\n- task: TaskRecord ID\n- exec: ExecutionRecord ID (for corrections or dependencies)\n- changelog: ChangelogRecord ID\n"
|
|
979
1385
|
}
|
|
980
1386
|
},
|
|
981
1387
|
examples: [
|
|
982
1388
|
{
|
|
983
|
-
id: "
|
|
984
|
-
taskId: "1752274500-task-
|
|
1389
|
+
id: "1752275500-exec-refactor-queries",
|
|
1390
|
+
taskId: "1752274500-task-optimizar-api",
|
|
985
1391
|
type: "progress",
|
|
986
|
-
title: "
|
|
987
|
-
result: "
|
|
988
|
-
notes: "
|
|
1392
|
+
title: "Refactor de queries N+1",
|
|
1393
|
+
result: "Refactorizados 3 queries N+1 a un solo JOIN optimizado. Performance mejor\xF3 de 2.5s a 200ms.",
|
|
1394
|
+
notes: "Identificados 3 N+1 queries en el endpoint /api/search. Aplicado eager loading y caching de relaciones.",
|
|
1395
|
+
references: [
|
|
1396
|
+
"commit:b2c3d4e",
|
|
1397
|
+
"file:src/api/search.ts"
|
|
1398
|
+
]
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
id: "1752361200-exec-api-externa-caida",
|
|
1402
|
+
taskId: "1752274500-task-optimizar-api",
|
|
1403
|
+
type: "blocker",
|
|
1404
|
+
title: "API Externa Ca\xEDda",
|
|
1405
|
+
result: "No se puede continuar con testing de integraci\xF3n. API de pagos devuelve 503.",
|
|
1406
|
+
notes: "La API de pagos de terceros (api.payments.com) est\xE1 devolviendo errores 503. Contactado soporte del proveedor. ETA de resoluci\xF3n: 2-3 horas.",
|
|
1407
|
+
references: [
|
|
1408
|
+
"url:https://status.payments.com"
|
|
1409
|
+
]
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
id: "1752188000-exec-plan-oauth-implementation",
|
|
1413
|
+
taskId: "1752274500-task-oauth-implementation",
|
|
1414
|
+
type: "analysis",
|
|
1415
|
+
title: "Plan de implementaci\xF3n OAuth2",
|
|
1416
|
+
result: "Documento de dise\xF1o t\xE9cnico completado. 5 sub-tareas identificadas con estimaciones de complejidad.",
|
|
1417
|
+
notes: "Evaluadas 3 opciones: NextAuth.js (elegida), Passport.js, custom implementation. NextAuth.js por madurez y soporte de m\xFAltiples providers.",
|
|
1418
|
+
references: [
|
|
1419
|
+
"file:docs/oauth-design.md"
|
|
1420
|
+
]
|
|
1421
|
+
},
|
|
1422
|
+
{
|
|
1423
|
+
id: "1752707800-exec-oauth-completed",
|
|
1424
|
+
taskId: "1752274500-task-oauth-implementation",
|
|
1425
|
+
type: "completion",
|
|
1426
|
+
title: "OAuth Implementation Completed",
|
|
1427
|
+
result: "Sistema OAuth2 completamente implementado, testeado y deployado a staging. 95% test coverage. Todos los acceptance criteria cumplidos.",
|
|
1428
|
+
notes: "Implementaci\xF3n finalizada. Code review aprobado. Tests E2E passing. Ready para changelog y deploy a producci\xF3n.",
|
|
1429
|
+
references: [
|
|
1430
|
+
"pr:456",
|
|
1431
|
+
"commit:def789abc",
|
|
1432
|
+
"url:https://staging.app.com/login"
|
|
1433
|
+
]
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
id: "1752275600-exec-cambio-estrategia-redis",
|
|
1437
|
+
taskId: "1752274500-task-oauth-implementation",
|
|
1438
|
+
type: "info",
|
|
1439
|
+
title: "Cambio de estrategia: Usar Redis para sessions",
|
|
1440
|
+
result: "Decisi\xF3n: Migrar de JWT stateless a sessions en Redis por requisito de revocaci\xF3n inmediata.",
|
|
1441
|
+
notes: "Durante code review se identific\xF3 requisito cr\xEDtico: revocar sesiones inmediatamente (ej: compromiso de cuenta). JWT stateless no permite esto sin lista negra compleja. Redis sessions permite revocaci\xF3n instant\xE1nea.",
|
|
1442
|
+
references: [
|
|
1443
|
+
"issue:567",
|
|
1444
|
+
"url:https://redis.io/docs/manual/keyspace-notifications/"
|
|
1445
|
+
]
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
id: "1752275700-exec-correccion-metricas",
|
|
1449
|
+
taskId: "1752274500-task-optimizar-api",
|
|
1450
|
+
type: "correction",
|
|
1451
|
+
title: "Correcci\xF3n: M\xE9tricas de performance",
|
|
1452
|
+
result: "Correcci\xF3n de execution 1752275500-exec-refactor-queries: El performance fue 200ms, no 50ms como se report\xF3.",
|
|
1453
|
+
notes: "Error de tipeo en execution original. La mejora real fue de 2.5s a 200ms (no 50ms). Sigue siendo significativa (92% mejora) pero n\xFAmeros correctos son importantes para m\xE9tricas.",
|
|
989
1454
|
references: [
|
|
990
|
-
"
|
|
1455
|
+
"exec:1752275500-exec-refactor-queries"
|
|
991
1456
|
]
|
|
992
1457
|
}
|
|
993
1458
|
]
|
|
@@ -998,7 +1463,7 @@ var feedback_record_schema_default = {
|
|
|
998
1463
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
999
1464
|
$id: "feedback_record_schema.json",
|
|
1000
1465
|
title: "FeedbackRecord",
|
|
1001
|
-
description: "Canonical schema for feedback records",
|
|
1466
|
+
description: "Canonical schema for feedback records - structured conversation about work",
|
|
1002
1467
|
additionalProperties: false,
|
|
1003
1468
|
type: "object",
|
|
1004
1469
|
required: [
|
|
@@ -1013,7 +1478,12 @@ var feedback_record_schema_default = {
|
|
|
1013
1478
|
id: {
|
|
1014
1479
|
type: "string",
|
|
1015
1480
|
pattern: "^\\d{10}-feedback-[a-z0-9-]{1,50}$",
|
|
1016
|
-
|
|
1481
|
+
maxLength: 70,
|
|
1482
|
+
description: "Unique identifier for the feedback entry",
|
|
1483
|
+
examples: [
|
|
1484
|
+
"1752788100-feedback-blocking-rest-api",
|
|
1485
|
+
"1752788200-feedback-question-test-coverage"
|
|
1486
|
+
]
|
|
1017
1487
|
},
|
|
1018
1488
|
entityType: {
|
|
1019
1489
|
type: "string",
|
|
@@ -1021,13 +1491,21 @@ var feedback_record_schema_default = {
|
|
|
1021
1491
|
"task",
|
|
1022
1492
|
"execution",
|
|
1023
1493
|
"changelog",
|
|
1024
|
-
"feedback"
|
|
1494
|
+
"feedback",
|
|
1495
|
+
"cycle"
|
|
1025
1496
|
],
|
|
1026
1497
|
description: "The type of entity this feedback refers to"
|
|
1027
1498
|
},
|
|
1028
1499
|
entityId: {
|
|
1029
1500
|
type: "string",
|
|
1030
|
-
|
|
1501
|
+
minLength: 1,
|
|
1502
|
+
maxLength: 256,
|
|
1503
|
+
description: "The ID of the entity this feedback refers to.\nMust match the pattern for its entityType:\n- task: ^\\d{10}-task-[a-z0-9-]{1,50}$\n- execution: ^\\d{10}-exec-[a-z0-9-]{1,50}$\n- changelog: ^\\d{10}-changelog-[a-z0-9-]{1,50}$\n- feedback: ^\\d{10}-feedback-[a-z0-9-]{1,50}$\n- cycle: ^\\d{10}-cycle-[a-z0-9-]{1,50}$\n",
|
|
1504
|
+
examples: [
|
|
1505
|
+
"1752274500-task-implementar-oauth",
|
|
1506
|
+
"1752642000-exec-subtarea-9-4",
|
|
1507
|
+
"1752788100-feedback-blocking-rest-api"
|
|
1508
|
+
]
|
|
1031
1509
|
},
|
|
1032
1510
|
type: {
|
|
1033
1511
|
type: "string",
|
|
@@ -1039,7 +1517,12 @@ var feedback_record_schema_default = {
|
|
|
1039
1517
|
"clarification",
|
|
1040
1518
|
"assignment"
|
|
1041
1519
|
],
|
|
1042
|
-
description: "The semantic intent of the feedback"
|
|
1520
|
+
description: "The semantic intent of the feedback",
|
|
1521
|
+
examples: [
|
|
1522
|
+
"blocking",
|
|
1523
|
+
"question",
|
|
1524
|
+
"approval"
|
|
1525
|
+
]
|
|
1043
1526
|
},
|
|
1044
1527
|
status: {
|
|
1045
1528
|
type: "string",
|
|
@@ -1049,31 +1532,69 @@ var feedback_record_schema_default = {
|
|
|
1049
1532
|
"resolved",
|
|
1050
1533
|
"wontfix"
|
|
1051
1534
|
],
|
|
1052
|
-
description:
|
|
1535
|
+
description: 'The lifecycle status of the feedback. \nNote: FeedbackRecords are immutable. To change status, create a new feedback \nthat references this one using entityType: "feedback" and resolvesFeedbackId.\n'
|
|
1053
1536
|
},
|
|
1054
1537
|
content: {
|
|
1055
1538
|
type: "string",
|
|
1056
1539
|
minLength: 1,
|
|
1057
|
-
maxLength:
|
|
1058
|
-
description: "The content of the feedback"
|
|
1540
|
+
maxLength: 5e3,
|
|
1541
|
+
description: "The content of the feedback. Reduced from 10000 to 5000 chars for practical use."
|
|
1059
1542
|
},
|
|
1060
1543
|
assignee: {
|
|
1061
1544
|
type: "string",
|
|
1062
|
-
|
|
1545
|
+
pattern: "^(human|agent)(:[a-z0-9-]+)+$",
|
|
1546
|
+
maxLength: 256,
|
|
1547
|
+
description: "Optional. The Actor ID responsible for addressing the feedback (e.g., 'human:maria', 'agent:camilo:cursor')",
|
|
1548
|
+
examples: [
|
|
1549
|
+
"human:maria",
|
|
1550
|
+
"agent:code-reviewer",
|
|
1551
|
+
"agent:camilo:cursor"
|
|
1552
|
+
]
|
|
1063
1553
|
},
|
|
1064
1554
|
resolvesFeedbackId: {
|
|
1065
1555
|
type: "string",
|
|
1066
|
-
|
|
1556
|
+
pattern: "^\\d{10}-feedback-[a-z0-9-]{1,50}$",
|
|
1557
|
+
maxLength: 70,
|
|
1558
|
+
description: "Optional. The ID of another feedback record that this one resolves or responds to",
|
|
1559
|
+
examples: [
|
|
1560
|
+
"1752788100-feedback-blocking-rest-api"
|
|
1561
|
+
]
|
|
1067
1562
|
}
|
|
1068
1563
|
},
|
|
1069
1564
|
examples: [
|
|
1070
1565
|
{
|
|
1071
|
-
id: "1752788100-feedback-blocking-
|
|
1566
|
+
id: "1752788100-feedback-blocking-rest-api",
|
|
1072
1567
|
entityType: "execution",
|
|
1073
1568
|
entityId: "1752642000-exec-subtarea-9-4",
|
|
1074
1569
|
type: "blocking",
|
|
1075
1570
|
status: "open",
|
|
1076
|
-
content: "Esta implementaci\xF3n no cumple el est\xE1ndar de rutas REST.
|
|
1571
|
+
content: "Esta implementaci\xF3n no cumple el est\xE1ndar de rutas REST. Los endpoints deben seguir el patr\xF3n /api/v1/{resource}/{id}. Actualmente usa /get-user?id=X que no es RESTful."
|
|
1572
|
+
},
|
|
1573
|
+
{
|
|
1574
|
+
id: "1752788200-feedback-rest-api-fixed",
|
|
1575
|
+
entityType: "feedback",
|
|
1576
|
+
entityId: "1752788100-feedback-blocking-rest-api",
|
|
1577
|
+
type: "clarification",
|
|
1578
|
+
status: "resolved",
|
|
1579
|
+
content: "Implementada la correcci\xF3n. Ahora todos los endpoints siguen el est\xE1ndar REST: GET /api/v1/users/:id, POST /api/v1/users, etc. Tests actualizados y passing.",
|
|
1580
|
+
resolvesFeedbackId: "1752788100-feedback-blocking-rest-api"
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
id: "1752788300-feedback-assign-auth-task",
|
|
1584
|
+
entityType: "task",
|
|
1585
|
+
entityId: "1752274500-task-implementar-oauth",
|
|
1586
|
+
type: "assignment",
|
|
1587
|
+
status: "open",
|
|
1588
|
+
content: "Asignando esta tarea a Mar\xEDa por su experiencia con OAuth2. Prioridad alta para el sprint actual.",
|
|
1589
|
+
assignee: "human:maria"
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
id: "1752788400-feedback-question-test-coverage",
|
|
1593
|
+
entityType: "task",
|
|
1594
|
+
entityId: "1752274500-task-implementar-oauth",
|
|
1595
|
+
type: "question",
|
|
1596
|
+
status: "open",
|
|
1597
|
+
content: "\xBFCu\xE1l es el nivel de test coverage esperado para esta feature? El spec no lo menciona expl\xEDcitamente. \xBFDebemos apuntar a 80% como el resto del proyecto?"
|
|
1077
1598
|
}
|
|
1078
1599
|
]
|
|
1079
1600
|
};
|
|
@@ -1091,15 +1612,14 @@ var task_record_schema_default = {
|
|
|
1091
1612
|
"title",
|
|
1092
1613
|
"status",
|
|
1093
1614
|
"priority",
|
|
1094
|
-
"description"
|
|
1095
|
-
"tags"
|
|
1615
|
+
"description"
|
|
1096
1616
|
],
|
|
1097
1617
|
properties: {
|
|
1098
1618
|
id: {
|
|
1099
1619
|
type: "string",
|
|
1100
1620
|
pattern: "^\\d{10}-task-[a-z0-9-]{1,50}$",
|
|
1101
|
-
maxLength:
|
|
1102
|
-
description: "Unique identifier for the task",
|
|
1621
|
+
maxLength: 66,
|
|
1622
|
+
description: "Unique identifier for the task (10 timestamp + 1 dash + 4 'task' + 1 dash + max 50 slug = 66 max)",
|
|
1103
1623
|
examples: [
|
|
1104
1624
|
"1752274500-task-implementar-auth",
|
|
1105
1625
|
"1752347700-task-fix-logging"
|
|
@@ -1115,9 +1635,12 @@ var task_record_schema_default = {
|
|
|
1115
1635
|
type: "array",
|
|
1116
1636
|
items: {
|
|
1117
1637
|
type: "string",
|
|
1118
|
-
|
|
1638
|
+
minLength: 1,
|
|
1639
|
+
pattern: "^\\d{10}-cycle-[a-z0-9-]{1,50}$",
|
|
1640
|
+
maxLength: 67
|
|
1119
1641
|
},
|
|
1120
|
-
|
|
1642
|
+
default: [],
|
|
1643
|
+
description: "Optional. The IDs of the strategic cycles this task belongs to. (10 timestamp + 1 dash + 5 'cycle' + 1 dash + max 50 slug = 67 max)"
|
|
1121
1644
|
},
|
|
1122
1645
|
status: {
|
|
1123
1646
|
type: "string",
|
|
@@ -1155,30 +1678,102 @@ var task_record_schema_default = {
|
|
|
1155
1678
|
type: "array",
|
|
1156
1679
|
items: {
|
|
1157
1680
|
type: "string",
|
|
1681
|
+
minLength: 1,
|
|
1158
1682
|
pattern: "^[a-z0-9-]+(:[a-z0-9-:]+)*$"
|
|
1159
1683
|
},
|
|
1160
|
-
|
|
1684
|
+
default: [],
|
|
1685
|
+
description: "Optional. List of key:value tags for categorization and role suggestion (e.g., 'skill:react', 'role:agent:developer')."
|
|
1161
1686
|
},
|
|
1162
1687
|
references: {
|
|
1163
1688
|
type: "array",
|
|
1164
1689
|
items: {
|
|
1165
1690
|
type: "string",
|
|
1691
|
+
minLength: 1,
|
|
1166
1692
|
maxLength: 500
|
|
1167
1693
|
},
|
|
1694
|
+
default: [],
|
|
1168
1695
|
description: "Valid links or files, when mentioned"
|
|
1169
1696
|
},
|
|
1170
1697
|
notes: {
|
|
1171
1698
|
type: "string",
|
|
1699
|
+
minLength: 0,
|
|
1172
1700
|
maxLength: 3e3,
|
|
1173
1701
|
description: "Additional comments, decisions made or added context"
|
|
1174
1702
|
}
|
|
1175
1703
|
},
|
|
1176
1704
|
examples: [
|
|
1177
1705
|
{
|
|
1178
|
-
id: "1752274500-task-
|
|
1179
|
-
|
|
1706
|
+
id: "1752274500-task-implement-oauth-flow",
|
|
1707
|
+
title: "Implement OAuth 2.0 authentication flow",
|
|
1708
|
+
status: "draft",
|
|
1180
1709
|
priority: "high",
|
|
1181
|
-
description: "
|
|
1710
|
+
description: "Implement complete OAuth 2.0 flow with GitHub provider. Include token refresh, session management, and secure storage. Must support both web and CLI flows.",
|
|
1711
|
+
cycleIds: [
|
|
1712
|
+
"1752270000-cycle-auth-mvp"
|
|
1713
|
+
],
|
|
1714
|
+
tags: [
|
|
1715
|
+
"skill:security",
|
|
1716
|
+
"category:feature",
|
|
1717
|
+
"package:core"
|
|
1718
|
+
],
|
|
1719
|
+
references: [
|
|
1720
|
+
"url:https://docs.github.com/en/apps/oauth-apps/building-oauth-apps"
|
|
1721
|
+
],
|
|
1722
|
+
notes: "Consider using proven library like passport.js or implement from scratch for learning."
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
id: "1752347700-task-fix-memory-leak-indexer",
|
|
1726
|
+
title: "Fix memory leak in indexer process",
|
|
1727
|
+
status: "active",
|
|
1728
|
+
priority: "critical",
|
|
1729
|
+
description: "Indexer process consuming >2GB RAM after 24h uptime. Profiling shows EventEmitter listeners not being cleaned up properly. Fix leak and add monitoring.",
|
|
1730
|
+
cycleIds: [],
|
|
1731
|
+
tags: [
|
|
1732
|
+
"category:bug",
|
|
1733
|
+
"skill:performance",
|
|
1734
|
+
"package:core"
|
|
1735
|
+
],
|
|
1736
|
+
references: [
|
|
1737
|
+
"file:packages/core/src/adapters/indexer_adapter/index.ts",
|
|
1738
|
+
"commit:a1b2c3d4",
|
|
1739
|
+
"issue:789"
|
|
1740
|
+
],
|
|
1741
|
+
notes: "Discovered during production monitoring. Affects long-running processes only."
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
id: "1752448900-task-add-typescript-linting",
|
|
1745
|
+
title: "Add TypeScript strict mode and ESLint rules",
|
|
1746
|
+
status: "done",
|
|
1747
|
+
priority: "medium",
|
|
1748
|
+
description: "Enable TypeScript strict mode across all packages. Configure ESLint with recommended rules. Fix all existing violations. Add pre-commit hook.",
|
|
1749
|
+
cycleIds: [
|
|
1750
|
+
"1752440000-cycle-code-quality-q1"
|
|
1751
|
+
],
|
|
1752
|
+
tags: [
|
|
1753
|
+
"category:tooling",
|
|
1754
|
+
"skill:typescript",
|
|
1755
|
+
"epic:code-quality"
|
|
1756
|
+
],
|
|
1757
|
+
references: [
|
|
1758
|
+
"pr:123",
|
|
1759
|
+
"file:.eslintrc.js",
|
|
1760
|
+
"file:tsconfig.json"
|
|
1761
|
+
],
|
|
1762
|
+
notes: "Approved by tech lead. All 47 violations fixed in PR #123."
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
id: "1752550000-task-research-graphql-migration",
|
|
1766
|
+
title: "Research GraphQL migration feasibility",
|
|
1767
|
+
status: "draft",
|
|
1768
|
+
priority: "low",
|
|
1769
|
+
description: "Evaluate feasibility of migrating REST API to GraphQL. Document pros/cons, effort estimation, and recommended approach. Focus on developer experience and performance.",
|
|
1770
|
+
cycleIds: [],
|
|
1771
|
+
tags: [
|
|
1772
|
+
"category:research",
|
|
1773
|
+
"skill:graphql",
|
|
1774
|
+
"skill:backend"
|
|
1775
|
+
],
|
|
1776
|
+
references: []
|
|
1182
1777
|
}
|
|
1183
1778
|
]
|
|
1184
1779
|
};
|
|
@@ -1260,7 +1855,7 @@ var workflow_methodology_record_schema_default = {
|
|
|
1260
1855
|
},
|
|
1261
1856
|
signatures: {
|
|
1262
1857
|
type: "object",
|
|
1263
|
-
description: "Signature requirements keyed by
|
|
1858
|
+
description: "Signature requirements keyed by role (e.g., 'approver:quality', 'developer:backend')",
|
|
1264
1859
|
additionalProperties: {
|
|
1265
1860
|
type: "object",
|
|
1266
1861
|
required: [
|
|
@@ -1428,27 +2023,35 @@ var workflow_methodology_record_schema_default = {
|
|
|
1428
2023
|
items: {
|
|
1429
2024
|
type: "object",
|
|
1430
2025
|
required: [
|
|
1431
|
-
"id",
|
|
1432
|
-
"gremio",
|
|
1433
2026
|
"engine"
|
|
1434
2027
|
],
|
|
2028
|
+
anyOf: [
|
|
2029
|
+
{
|
|
2030
|
+
required: [
|
|
2031
|
+
"id"
|
|
2032
|
+
]
|
|
2033
|
+
},
|
|
2034
|
+
{
|
|
2035
|
+
required: [
|
|
2036
|
+
"required_roles"
|
|
2037
|
+
]
|
|
2038
|
+
}
|
|
2039
|
+
],
|
|
1435
2040
|
additionalProperties: false,
|
|
1436
2041
|
properties: {
|
|
1437
2042
|
id: {
|
|
1438
2043
|
type: "string",
|
|
1439
2044
|
pattern: "^agent:[a-z0-9:-]+$",
|
|
1440
|
-
description: "
|
|
2045
|
+
description: "Optional: Specific agent ID. If provided, uses this exact agent."
|
|
1441
2046
|
},
|
|
1442
|
-
|
|
1443
|
-
type: "
|
|
1444
|
-
|
|
1445
|
-
"
|
|
1446
|
-
"
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
],
|
|
1451
|
-
description: "Agent guild classification"
|
|
2047
|
+
required_roles: {
|
|
2048
|
+
type: "array",
|
|
2049
|
+
items: {
|
|
2050
|
+
type: "string",
|
|
2051
|
+
pattern: "^[a-z0-9-]+(:[a-z0-9-]+)*$"
|
|
2052
|
+
},
|
|
2053
|
+
minItems: 1,
|
|
2054
|
+
description: "Optional: Required capability roles. Matches any agent with these roles (from ActorRecord)."
|
|
1452
2055
|
},
|
|
1453
2056
|
engine: {
|
|
1454
2057
|
type: "object",
|
|
@@ -1575,7 +2178,257 @@ var workflow_methodology_record_schema_default = {
|
|
|
1575
2178
|
}
|
|
1576
2179
|
}
|
|
1577
2180
|
}
|
|
1578
|
-
}
|
|
2181
|
+
},
|
|
2182
|
+
examples: [
|
|
2183
|
+
{
|
|
2184
|
+
version: "1.0.0",
|
|
2185
|
+
name: "Simple Kanban",
|
|
2186
|
+
description: "Basic workflow for small teams",
|
|
2187
|
+
state_transitions: {
|
|
2188
|
+
review: {
|
|
2189
|
+
from: [
|
|
2190
|
+
"draft"
|
|
2191
|
+
],
|
|
2192
|
+
requires: {
|
|
2193
|
+
command: "gitgov task submit"
|
|
2194
|
+
}
|
|
2195
|
+
},
|
|
2196
|
+
ready: {
|
|
2197
|
+
from: [
|
|
2198
|
+
"review"
|
|
2199
|
+
],
|
|
2200
|
+
requires: {
|
|
2201
|
+
command: "gitgov task approve",
|
|
2202
|
+
signatures: {
|
|
2203
|
+
__default__: {
|
|
2204
|
+
role: "approver",
|
|
2205
|
+
capability_roles: [
|
|
2206
|
+
"approver:product"
|
|
2207
|
+
],
|
|
2208
|
+
min_approvals: 1
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
},
|
|
2213
|
+
active: {
|
|
2214
|
+
from: [
|
|
2215
|
+
"ready"
|
|
2216
|
+
],
|
|
2217
|
+
requires: {
|
|
2218
|
+
event: "first_execution_record_created"
|
|
2219
|
+
}
|
|
2220
|
+
},
|
|
2221
|
+
done: {
|
|
2222
|
+
from: [
|
|
2223
|
+
"active"
|
|
2224
|
+
],
|
|
2225
|
+
requires: {
|
|
2226
|
+
command: "gitgov task complete"
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
},
|
|
2230
|
+
view_configs: {
|
|
2231
|
+
"kanban-3col": {
|
|
2232
|
+
columns: {
|
|
2233
|
+
"To Do": [
|
|
2234
|
+
"draft",
|
|
2235
|
+
"review"
|
|
2236
|
+
],
|
|
2237
|
+
"In Progress": [
|
|
2238
|
+
"ready",
|
|
2239
|
+
"active"
|
|
2240
|
+
],
|
|
2241
|
+
Done: [
|
|
2242
|
+
"done"
|
|
2243
|
+
]
|
|
2244
|
+
},
|
|
2245
|
+
theme: "minimal",
|
|
2246
|
+
layout: "horizontal"
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
},
|
|
2250
|
+
{
|
|
2251
|
+
version: "1.0.0",
|
|
2252
|
+
name: "GitGovernance Default Methodology",
|
|
2253
|
+
description: "Standard GitGovernance workflow with quality gates and agent collaboration",
|
|
2254
|
+
state_transitions: {
|
|
2255
|
+
review: {
|
|
2256
|
+
from: [
|
|
2257
|
+
"draft"
|
|
2258
|
+
],
|
|
2259
|
+
requires: {
|
|
2260
|
+
command: "gitgov task submit",
|
|
2261
|
+
signatures: {
|
|
2262
|
+
__default__: {
|
|
2263
|
+
role: "submitter",
|
|
2264
|
+
capability_roles: [
|
|
2265
|
+
"author"
|
|
2266
|
+
],
|
|
2267
|
+
min_approvals: 1
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
},
|
|
2272
|
+
ready: {
|
|
2273
|
+
from: [
|
|
2274
|
+
"review"
|
|
2275
|
+
],
|
|
2276
|
+
requires: {
|
|
2277
|
+
command: "gitgov task approve",
|
|
2278
|
+
signatures: {
|
|
2279
|
+
__default__: {
|
|
2280
|
+
role: "approver",
|
|
2281
|
+
capability_roles: [
|
|
2282
|
+
"approver:product"
|
|
2283
|
+
],
|
|
2284
|
+
min_approvals: 1
|
|
2285
|
+
},
|
|
2286
|
+
design: {
|
|
2287
|
+
role: "approver",
|
|
2288
|
+
capability_roles: [
|
|
2289
|
+
"approver:design"
|
|
2290
|
+
],
|
|
2291
|
+
min_approvals: 1
|
|
2292
|
+
},
|
|
2293
|
+
quality: {
|
|
2294
|
+
role: "approver",
|
|
2295
|
+
capability_roles: [
|
|
2296
|
+
"approver:quality"
|
|
2297
|
+
],
|
|
2298
|
+
min_approvals: 1
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
},
|
|
2303
|
+
active: {
|
|
2304
|
+
from: [
|
|
2305
|
+
"ready",
|
|
2306
|
+
"paused"
|
|
2307
|
+
],
|
|
2308
|
+
requires: {
|
|
2309
|
+
event: "first_execution_record_created",
|
|
2310
|
+
custom_rules: [
|
|
2311
|
+
"task_must_have_valid_assignment_for_executor"
|
|
2312
|
+
]
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
2315
|
+
done: {
|
|
2316
|
+
from: [
|
|
2317
|
+
"active"
|
|
2318
|
+
],
|
|
2319
|
+
requires: {
|
|
2320
|
+
command: "gitgov task complete",
|
|
2321
|
+
signatures: {
|
|
2322
|
+
__default__: {
|
|
2323
|
+
role: "approver",
|
|
2324
|
+
capability_roles: [
|
|
2325
|
+
"approver:quality"
|
|
2326
|
+
],
|
|
2327
|
+
min_approvals: 1
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
},
|
|
2332
|
+
archived: {
|
|
2333
|
+
from: [
|
|
2334
|
+
"done"
|
|
2335
|
+
],
|
|
2336
|
+
requires: {
|
|
2337
|
+
event: "changelog_record_created"
|
|
2338
|
+
}
|
|
2339
|
+
},
|
|
2340
|
+
paused: {
|
|
2341
|
+
from: [
|
|
2342
|
+
"active",
|
|
2343
|
+
"review"
|
|
2344
|
+
],
|
|
2345
|
+
requires: {
|
|
2346
|
+
event: "feedback_blocking_created"
|
|
2347
|
+
}
|
|
2348
|
+
},
|
|
2349
|
+
discarded: {
|
|
2350
|
+
from: [
|
|
2351
|
+
"ready",
|
|
2352
|
+
"active"
|
|
2353
|
+
],
|
|
2354
|
+
requires: {
|
|
2355
|
+
command: "gitgov task cancel",
|
|
2356
|
+
signatures: {
|
|
2357
|
+
__default__: {
|
|
2358
|
+
role: "canceller",
|
|
2359
|
+
capability_roles: [
|
|
2360
|
+
"approver:product",
|
|
2361
|
+
"approver:quality"
|
|
2362
|
+
],
|
|
2363
|
+
min_approvals: 1
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
},
|
|
2369
|
+
custom_rules: {
|
|
2370
|
+
task_must_have_valid_assignment_for_executor: {
|
|
2371
|
+
description: "Task must have a valid assignment before execution can begin",
|
|
2372
|
+
validation: "assignment_required"
|
|
2373
|
+
}
|
|
2374
|
+
},
|
|
2375
|
+
view_configs: {
|
|
2376
|
+
"kanban-4col": {
|
|
2377
|
+
columns: {
|
|
2378
|
+
Draft: [
|
|
2379
|
+
"draft"
|
|
2380
|
+
],
|
|
2381
|
+
"In Progress": [
|
|
2382
|
+
"review",
|
|
2383
|
+
"ready",
|
|
2384
|
+
"active"
|
|
2385
|
+
],
|
|
2386
|
+
Review: [
|
|
2387
|
+
"done"
|
|
2388
|
+
],
|
|
2389
|
+
Done: [
|
|
2390
|
+
"archived"
|
|
2391
|
+
],
|
|
2392
|
+
Cancelled: [
|
|
2393
|
+
"discarded"
|
|
2394
|
+
]
|
|
2395
|
+
},
|
|
2396
|
+
theme: "minimal",
|
|
2397
|
+
layout: "horizontal"
|
|
2398
|
+
},
|
|
2399
|
+
"kanban-7col": {
|
|
2400
|
+
columns: {
|
|
2401
|
+
Draft: [
|
|
2402
|
+
"draft"
|
|
2403
|
+
],
|
|
2404
|
+
Review: [
|
|
2405
|
+
"review"
|
|
2406
|
+
],
|
|
2407
|
+
Ready: [
|
|
2408
|
+
"ready"
|
|
2409
|
+
],
|
|
2410
|
+
Active: [
|
|
2411
|
+
"active"
|
|
2412
|
+
],
|
|
2413
|
+
Done: [
|
|
2414
|
+
"done"
|
|
2415
|
+
],
|
|
2416
|
+
Archived: [
|
|
2417
|
+
"archived"
|
|
2418
|
+
],
|
|
2419
|
+
Blocked: [
|
|
2420
|
+
"paused"
|
|
2421
|
+
],
|
|
2422
|
+
Cancelled: [
|
|
2423
|
+
"discarded"
|
|
2424
|
+
]
|
|
2425
|
+
},
|
|
2426
|
+
theme: "corporate",
|
|
2427
|
+
layout: "vertical"
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
]
|
|
1579
2432
|
};
|
|
1580
2433
|
|
|
1581
2434
|
// src/schemas/generated/index.ts
|
|
@@ -1717,13 +2570,13 @@ var SchemaValidationError = class extends Error {
|
|
|
1717
2570
|
}
|
|
1718
2571
|
};
|
|
1719
2572
|
var DetailedValidationError = class extends GitGovError {
|
|
1720
|
-
constructor(recordType,
|
|
1721
|
-
const errorSummary =
|
|
2573
|
+
constructor(recordType, errors) {
|
|
2574
|
+
const errorSummary = errors.map((err) => `${err.field}: ${err.message}`).join(", ");
|
|
1722
2575
|
super(
|
|
1723
2576
|
`${recordType} validation failed: ${errorSummary}`,
|
|
1724
2577
|
"DETAILED_VALIDATION_ERROR"
|
|
1725
2578
|
);
|
|
1726
|
-
this.
|
|
2579
|
+
this.errors = errors;
|
|
1727
2580
|
}
|
|
1728
2581
|
};
|
|
1729
2582
|
|
|
@@ -1827,10 +2680,10 @@ async function generateKeys() {
|
|
|
1827
2680
|
privateKey: Buffer.from(privateKey).toString("base64")
|
|
1828
2681
|
};
|
|
1829
2682
|
}
|
|
1830
|
-
function signPayload(payload, privateKey, keyId, role) {
|
|
2683
|
+
function signPayload(payload, privateKey, keyId, role, notes) {
|
|
1831
2684
|
const payloadChecksum = calculatePayloadChecksum(payload);
|
|
1832
2685
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
1833
|
-
const digest = `${payloadChecksum}:${keyId}:${role}:${timestamp}`;
|
|
2686
|
+
const digest = `${payloadChecksum}:${keyId}:${role}:${notes}:${timestamp}`;
|
|
1834
2687
|
const digestHash = createHash("sha256").update(digest).digest();
|
|
1835
2688
|
const signature = sign(null, digestHash, {
|
|
1836
2689
|
key: Buffer.from(privateKey, "base64"),
|
|
@@ -1840,9 +2693,9 @@ function signPayload(payload, privateKey, keyId, role) {
|
|
|
1840
2693
|
return {
|
|
1841
2694
|
keyId,
|
|
1842
2695
|
role,
|
|
2696
|
+
notes,
|
|
1843
2697
|
signature: signature.toString("base64"),
|
|
1844
|
-
timestamp
|
|
1845
|
-
timestamp_iso: new Date(timestamp * 1e3).toISOString()
|
|
2698
|
+
timestamp
|
|
1846
2699
|
};
|
|
1847
2700
|
}
|
|
1848
2701
|
async function verifySignatures(record, getActorPublicKey) {
|
|
@@ -1852,7 +2705,7 @@ async function verifySignatures(record, getActorPublicKey) {
|
|
|
1852
2705
|
logger2.warn(`Public key not found for actor: ${signature.keyId}`);
|
|
1853
2706
|
return false;
|
|
1854
2707
|
}
|
|
1855
|
-
const digest = `${record.header.payloadChecksum}:${signature.keyId}:${signature.role}:${signature.timestamp}`;
|
|
2708
|
+
const digest = `${record.header.payloadChecksum}:${signature.keyId}:${signature.role}:${signature.notes}:${signature.timestamp}`;
|
|
1856
2709
|
const digestHash = createHash("sha256").update(digest).digest();
|
|
1857
2710
|
const isValid = verify(
|
|
1858
2711
|
null,
|
|
@@ -1955,13 +2808,6 @@ function validateEmbeddedMetadataBusinessRules(data) {
|
|
|
1955
2808
|
value: data.header.signatures
|
|
1956
2809
|
});
|
|
1957
2810
|
}
|
|
1958
|
-
if (data.header.audit && (data.header.audit.length < 1 || data.header.audit.length > 3e3)) {
|
|
1959
|
-
errors.push({
|
|
1960
|
-
field: "header.audit",
|
|
1961
|
-
message: "audit field must be between 1 and 3000 characters",
|
|
1962
|
-
value: data.header.audit
|
|
1963
|
-
});
|
|
1964
|
-
}
|
|
1965
2811
|
return {
|
|
1966
2812
|
isValid: errors.length === 0,
|
|
1967
2813
|
errors
|
|
@@ -1981,8 +2827,8 @@ function isTaskRecord(data) {
|
|
|
1981
2827
|
function validateTaskRecordDetailed(data) {
|
|
1982
2828
|
const [isValid, ajvErrors] = validateTaskRecordSchema(data);
|
|
1983
2829
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
1984
|
-
field: error.instancePath || error.
|
|
1985
|
-
message: error.message || "
|
|
2830
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2831
|
+
message: error.message || "Unknown validation error",
|
|
1986
2832
|
value: error.data
|
|
1987
2833
|
})) : [];
|
|
1988
2834
|
return {
|
|
@@ -1993,9 +2839,12 @@ function validateTaskRecordDetailed(data) {
|
|
|
1993
2839
|
async function validateFullTaskRecord(record, getActorPublicKey) {
|
|
1994
2840
|
const [isValidSchema, errors] = validateTaskRecordSchema(record.payload);
|
|
1995
2841
|
if (!isValidSchema) {
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2842
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
2843
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2844
|
+
message: error.message || "Unknown validation error",
|
|
2845
|
+
value: error.data
|
|
2846
|
+
}));
|
|
2847
|
+
throw new DetailedValidationError("TaskRecord", formattedErrors);
|
|
1999
2848
|
}
|
|
2000
2849
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2001
2850
|
}
|
|
@@ -2020,32 +2869,14 @@ function generateExecutionId(title, timestamp) {
|
|
|
2020
2869
|
const slug = sanitizeForId(title);
|
|
2021
2870
|
return `${timestamp}-exec-${slug}`;
|
|
2022
2871
|
}
|
|
2023
|
-
function generateChangelogId(
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
entitySlug = sanitizeForId(entityId);
|
|
2027
|
-
} else {
|
|
2028
|
-
const parsed = parseTimestampedId(entityId);
|
|
2029
|
-
entitySlug = parsed ? parsed.slug : sanitizeForId(entityId);
|
|
2030
|
-
}
|
|
2031
|
-
return `${timestamp}-changelog-${entityType}-${entitySlug}`;
|
|
2872
|
+
function generateChangelogId(title, timestamp) {
|
|
2873
|
+
const slug = sanitizeForId(title);
|
|
2874
|
+
return `${timestamp}-changelog-${slug}`;
|
|
2032
2875
|
}
|
|
2033
2876
|
function generateFeedbackId(title, timestamp) {
|
|
2034
2877
|
const slug = sanitizeForId(title);
|
|
2035
2878
|
return `${timestamp}-feedback-${slug}`;
|
|
2036
2879
|
}
|
|
2037
|
-
function parseTimestampedId(id) {
|
|
2038
|
-
if (typeof id !== "string") return null;
|
|
2039
|
-
const match = id.match(/^(\d+)-(\w+)-(.+)$/);
|
|
2040
|
-
if (!match || !match[1] || !match[2] || !match[3]) {
|
|
2041
|
-
return null;
|
|
2042
|
-
}
|
|
2043
|
-
return {
|
|
2044
|
-
timestamp: parseInt(match[1], 10),
|
|
2045
|
-
prefix: match[2],
|
|
2046
|
-
slug: match[3]
|
|
2047
|
-
};
|
|
2048
|
-
}
|
|
2049
2880
|
|
|
2050
2881
|
// src/factories/task_factory.ts
|
|
2051
2882
|
async function createTaskRecord(payload) {
|
|
@@ -2083,8 +2914,8 @@ function isCycleRecord(data) {
|
|
|
2083
2914
|
function validateCycleRecordDetailed(data) {
|
|
2084
2915
|
const [isValid, ajvErrors] = validateCycleRecordSchema(data);
|
|
2085
2916
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
2086
|
-
field: error.instancePath || error.
|
|
2087
|
-
message: error.message || "
|
|
2917
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2918
|
+
message: error.message || "Unknown validation error",
|
|
2088
2919
|
value: error.data
|
|
2089
2920
|
})) : [];
|
|
2090
2921
|
return {
|
|
@@ -2095,9 +2926,12 @@ function validateCycleRecordDetailed(data) {
|
|
|
2095
2926
|
async function validateFullCycleRecord(record, getActorPublicKey) {
|
|
2096
2927
|
const [isValidSchema, errors] = validateCycleRecordSchema(record.payload);
|
|
2097
2928
|
if (!isValidSchema) {
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2929
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
2930
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2931
|
+
message: error.message || "Unknown validation error",
|
|
2932
|
+
value: error.data
|
|
2933
|
+
}));
|
|
2934
|
+
throw new DetailedValidationError("CycleRecord", formattedErrors);
|
|
2101
2935
|
}
|
|
2102
2936
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2103
2937
|
}
|
|
@@ -2395,8 +3229,8 @@ function isActorRecord(data) {
|
|
|
2395
3229
|
function validateActorRecordDetailed(data) {
|
|
2396
3230
|
const [isValid, ajvErrors] = validateActorRecordSchema(data);
|
|
2397
3231
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
2398
|
-
field: error.instancePath || error.
|
|
2399
|
-
message: error.message || "
|
|
3232
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3233
|
+
message: error.message || "Unknown validation error",
|
|
2400
3234
|
value: error.data
|
|
2401
3235
|
})) : [];
|
|
2402
3236
|
return {
|
|
@@ -2406,10 +3240,13 @@ function validateActorRecordDetailed(data) {
|
|
|
2406
3240
|
}
|
|
2407
3241
|
async function validateFullActorRecord(record, getActorPublicKey) {
|
|
2408
3242
|
const [isValidSchema, errors] = validateActorRecordSchema(record.payload);
|
|
2409
|
-
if (!isValidSchema) {
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
3243
|
+
if (!isValidSchema) {
|
|
3244
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
3245
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3246
|
+
message: error.message || "Unknown validation error",
|
|
3247
|
+
value: error.data
|
|
3248
|
+
}));
|
|
3249
|
+
throw new DetailedValidationError("ActorRecord", formattedErrors);
|
|
2413
3250
|
}
|
|
2414
3251
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2415
3252
|
}
|
|
@@ -2445,8 +3282,8 @@ function isAgentRecord(data) {
|
|
|
2445
3282
|
function validateAgentRecordDetailed(data) {
|
|
2446
3283
|
const [isValid, ajvErrors] = validateAgentRecordSchema(data);
|
|
2447
3284
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
2448
|
-
field: error.instancePath || error.
|
|
2449
|
-
message: error.message || "
|
|
3285
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3286
|
+
message: error.message || "Unknown validation error",
|
|
2450
3287
|
value: error.data
|
|
2451
3288
|
})) : [];
|
|
2452
3289
|
return {
|
|
@@ -2457,9 +3294,12 @@ function validateAgentRecordDetailed(data) {
|
|
|
2457
3294
|
async function validateFullAgentRecord(record, getActorPublicKey) {
|
|
2458
3295
|
const [isValidSchema, errors] = validateAgentRecordSchema(record.payload);
|
|
2459
3296
|
if (!isValidSchema) {
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
3297
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
3298
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3299
|
+
message: error.message || "Unknown validation error",
|
|
3300
|
+
value: error.data
|
|
3301
|
+
}));
|
|
3302
|
+
throw new DetailedValidationError("AgentRecord", formattedErrors);
|
|
2463
3303
|
}
|
|
2464
3304
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2465
3305
|
}
|
|
@@ -2486,7 +3326,6 @@ async function validateAgentActorRelationship(agentRecord, getEffectiveActor) {
|
|
|
2486
3326
|
async function createAgentRecord(payload) {
|
|
2487
3327
|
const agent = {
|
|
2488
3328
|
id: payload.id || "",
|
|
2489
|
-
guild: payload.guild || "design",
|
|
2490
3329
|
engine: payload.engine || { type: "local" },
|
|
2491
3330
|
status: payload.status || "active",
|
|
2492
3331
|
triggers: payload.triggers || [],
|
|
@@ -2539,7 +3378,7 @@ var IdentityAdapter = class {
|
|
|
2539
3378
|
};
|
|
2540
3379
|
const validatedPayload = await createActorRecord(completePayload);
|
|
2541
3380
|
const payloadChecksum = calculatePayloadChecksum(validatedPayload);
|
|
2542
|
-
const signature = await signPayload(validatedPayload, privateKey, actorId, "author");
|
|
3381
|
+
const signature = await signPayload(validatedPayload, privateKey, actorId, "author", "Actor registration");
|
|
2543
3382
|
const record = {
|
|
2544
3383
|
header: {
|
|
2545
3384
|
version: "1.0",
|
|
@@ -2601,9 +3440,9 @@ var IdentityAdapter = class {
|
|
|
2601
3440
|
const mockSignature = {
|
|
2602
3441
|
keyId: actorId,
|
|
2603
3442
|
role,
|
|
3443
|
+
notes: "Record signed",
|
|
2604
3444
|
signature: `mock-signature-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2605
|
-
timestamp: Math.floor(Date.now() / 1e3)
|
|
2606
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
3445
|
+
timestamp: Math.floor(Date.now() / 1e3)
|
|
2607
3446
|
};
|
|
2608
3447
|
const signedRecord = {
|
|
2609
3448
|
...record,
|
|
@@ -2711,8 +3550,8 @@ var IdentityAdapter = class {
|
|
|
2711
3550
|
console.warn("authenticate not fully implemented yet");
|
|
2712
3551
|
}
|
|
2713
3552
|
async createAgentRecord(payload) {
|
|
2714
|
-
if (!payload.id || !payload.
|
|
2715
|
-
throw new Error("AgentRecord requires id
|
|
3553
|
+
if (!payload.id || !payload.engine) {
|
|
3554
|
+
throw new Error("AgentRecord requires id and engine");
|
|
2716
3555
|
}
|
|
2717
3556
|
const correspondingActor = await this.getActor(payload.id);
|
|
2718
3557
|
if (!correspondingActor) {
|
|
@@ -2723,7 +3562,6 @@ var IdentityAdapter = class {
|
|
|
2723
3562
|
}
|
|
2724
3563
|
const completePayload = {
|
|
2725
3564
|
id: payload.id,
|
|
2726
|
-
guild: payload.guild,
|
|
2727
3565
|
engine: payload.engine,
|
|
2728
3566
|
status: payload.status || "active",
|
|
2729
3567
|
triggers: payload.triggers || [],
|
|
@@ -2733,7 +3571,7 @@ var IdentityAdapter = class {
|
|
|
2733
3571
|
};
|
|
2734
3572
|
const validatedPayload = await createAgentRecord(completePayload);
|
|
2735
3573
|
const payloadChecksum = calculatePayloadChecksum(validatedPayload);
|
|
2736
|
-
const signature = signPayload(validatedPayload, "placeholder-private-key", payload.id, "author");
|
|
3574
|
+
const signature = signPayload(validatedPayload, "placeholder-private-key", payload.id, "author", "Agent registration");
|
|
2737
3575
|
const record = {
|
|
2738
3576
|
header: {
|
|
2739
3577
|
version: "1.0",
|
|
@@ -2758,7 +3596,6 @@ var IdentityAdapter = class {
|
|
|
2758
3596
|
source: "identity_adapter",
|
|
2759
3597
|
payload: {
|
|
2760
3598
|
agentId: validatedPayload.id,
|
|
2761
|
-
guild: validatedPayload.guild,
|
|
2762
3599
|
engine: validatedPayload.engine,
|
|
2763
3600
|
correspondingActorId: correspondingActor.id
|
|
2764
3601
|
}
|
|
@@ -2863,11 +3700,11 @@ var FeedbackAdapter = class {
|
|
|
2863
3700
|
this.eventBus = dependencies.eventBus;
|
|
2864
3701
|
}
|
|
2865
3702
|
/**
|
|
2866
|
-
* [EARS-1] Creates a new FeedbackRecord for structured communication between actors.
|
|
3703
|
+
* [EARS-1, EARS-2, EARS-3, EARS-4, EARS-5, EARS-6, EARS-7, EARS-8] Creates a new FeedbackRecord for structured communication between actors.
|
|
2867
3704
|
*
|
|
2868
3705
|
* Description: Creates a new FeedbackRecord for structured communication between actors.
|
|
2869
3706
|
* Implementation: Builds record with status: "open", signs with actorId, persists and emits event.
|
|
2870
|
-
* Usage: Invoked by `gitgov feedback
|
|
3707
|
+
* Usage: Invoked by `gitgov feedback create` to create feedback, assignments, blocks, responses.
|
|
2871
3708
|
* Returns: Complete and signed FeedbackRecord.
|
|
2872
3709
|
*/
|
|
2873
3710
|
async create(payload, actorId) {
|
|
@@ -2875,21 +3712,30 @@ var FeedbackAdapter = class {
|
|
|
2875
3712
|
if (!payloadWithEntityId.entityId) {
|
|
2876
3713
|
throw new Error("RecordNotFoundError: entityId is required");
|
|
2877
3714
|
}
|
|
2878
|
-
if (payloadWithEntityId.entityType && !["task", "execution", "changelog", "feedback"].includes(payloadWithEntityId.entityType)) {
|
|
2879
|
-
throw new Error("InvalidEntityTypeError: entityType must be task, execution, changelog, or
|
|
3715
|
+
if (payloadWithEntityId.entityType && !["task", "execution", "changelog", "feedback", "cycle"].includes(payloadWithEntityId.entityType)) {
|
|
3716
|
+
throw new Error("InvalidEntityTypeError: entityType must be task, execution, changelog, feedback, or cycle");
|
|
2880
3717
|
}
|
|
2881
3718
|
if (payload.type === "assignment" && payload.assignee) {
|
|
2882
3719
|
const existingFeedbacks = await this.getFeedbackByEntity(payloadWithEntityId.entityId);
|
|
2883
|
-
const
|
|
3720
|
+
const openAssignments = existingFeedbacks.filter(
|
|
2884
3721
|
(feedback) => feedback.type === "assignment" && feedback.assignee === payload.assignee && feedback.status === "open"
|
|
2885
3722
|
);
|
|
2886
|
-
if (
|
|
2887
|
-
|
|
3723
|
+
if (openAssignments.length > 0) {
|
|
3724
|
+
const allFeedbacks = await this.getAllFeedback();
|
|
3725
|
+
for (const assignment of openAssignments) {
|
|
3726
|
+
const hasResolution = allFeedbacks.some(
|
|
3727
|
+
(feedback) => feedback.entityType === "feedback" && feedback.resolvesFeedbackId === assignment.id && feedback.status === "resolved"
|
|
3728
|
+
);
|
|
3729
|
+
if (!hasResolution) {
|
|
3730
|
+
throw new Error(`DuplicateAssignmentError: Task ${payloadWithEntityId.entityId} is already assigned to ${payload.assignee} (feedback: ${assignment.id})`);
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
2888
3733
|
}
|
|
2889
3734
|
}
|
|
2890
3735
|
const enrichedPayload = {
|
|
2891
|
-
|
|
2892
|
-
|
|
3736
|
+
status: "open",
|
|
3737
|
+
...payload
|
|
3738
|
+
// Allows payload.status to override default
|
|
2893
3739
|
};
|
|
2894
3740
|
try {
|
|
2895
3741
|
const validatedPayload = await createFeedbackRecord(enrichedPayload);
|
|
@@ -2901,9 +3747,9 @@ var FeedbackAdapter = class {
|
|
|
2901
3747
|
signatures: [{
|
|
2902
3748
|
keyId: actorId,
|
|
2903
3749
|
role: "author",
|
|
3750
|
+
notes: "Feedback created",
|
|
2904
3751
|
signature: "placeholder",
|
|
2905
|
-
timestamp: Date.now()
|
|
2906
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
3752
|
+
timestamp: Date.now()
|
|
2907
3753
|
}]
|
|
2908
3754
|
},
|
|
2909
3755
|
payload: validatedPayload
|
|
@@ -2922,7 +3768,8 @@ var FeedbackAdapter = class {
|
|
|
2922
3768
|
status: validatedPayload.status,
|
|
2923
3769
|
content: validatedPayload.content,
|
|
2924
3770
|
triggeredBy: actorId,
|
|
2925
|
-
assignee: validatedPayload.assignee
|
|
3771
|
+
assignee: validatedPayload.assignee,
|
|
3772
|
+
resolvesFeedbackId: validatedPayload.resolvesFeedbackId
|
|
2926
3773
|
}
|
|
2927
3774
|
});
|
|
2928
3775
|
return validatedPayload;
|
|
@@ -2934,51 +3781,30 @@ var FeedbackAdapter = class {
|
|
|
2934
3781
|
}
|
|
2935
3782
|
}
|
|
2936
3783
|
/**
|
|
2937
|
-
* [EARS-
|
|
3784
|
+
* [EARS-9, EARS-10, EARS-11, EARS-12] Helper: Creates a new feedback that "resolves" another (immutable).
|
|
2938
3785
|
*
|
|
2939
|
-
* Description:
|
|
2940
|
-
* Implementation:
|
|
2941
|
-
* Usage:
|
|
2942
|
-
* Returns:
|
|
2943
|
-
*/
|
|
2944
|
-
async resolve(feedbackId, actorId) {
|
|
2945
|
-
const
|
|
2946
|
-
if (!
|
|
3786
|
+
* Description: Helper method that creates a new feedback documenting resolution of another feedback.
|
|
3787
|
+
* Implementation: Verifies original exists, then delegates to create() with immutable pattern.
|
|
3788
|
+
* Usage: Ergonomic helper for common case. For advanced cases (wontfix, approval), use create() directly.
|
|
3789
|
+
* Returns: New FeedbackRecord that points to the original with resolvesFeedbackId.
|
|
3790
|
+
*/
|
|
3791
|
+
async resolve(feedbackId, actorId, content) {
|
|
3792
|
+
const originalFeedback = await this.getFeedback(feedbackId);
|
|
3793
|
+
if (!originalFeedback) {
|
|
2947
3794
|
throw new Error(`RecordNotFoundError: Feedback not found: ${feedbackId}`);
|
|
2948
3795
|
}
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
status: "resolved"
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
...existingRecord,
|
|
2959
|
-
payload: updatedPayload
|
|
2960
|
-
};
|
|
2961
|
-
const signedRecord = await this.identity.signRecord(updatedRecord, actorId, "resolver");
|
|
2962
|
-
await this.feedbackStore.write(signedRecord);
|
|
2963
|
-
this.eventBus.publish({
|
|
2964
|
-
type: "feedback.status.changed",
|
|
2965
|
-
timestamp: Date.now(),
|
|
2966
|
-
source: "feedback_adapter",
|
|
2967
|
-
payload: {
|
|
2968
|
-
feedbackId: updatedPayload.id,
|
|
2969
|
-
oldStatus: existingRecord.payload.status,
|
|
2970
|
-
newStatus: updatedPayload.status,
|
|
2971
|
-
triggeredBy: actorId,
|
|
2972
|
-
assignee: updatedPayload.assignee
|
|
2973
|
-
}
|
|
2974
|
-
});
|
|
2975
|
-
return updatedPayload;
|
|
2976
|
-
} catch (error) {
|
|
2977
|
-
throw error;
|
|
2978
|
-
}
|
|
3796
|
+
const resolveContent = content || `Feedback resolved by ${actorId}`;
|
|
3797
|
+
return await this.create({
|
|
3798
|
+
entityType: "feedback",
|
|
3799
|
+
entityId: feedbackId,
|
|
3800
|
+
type: "clarification",
|
|
3801
|
+
status: "resolved",
|
|
3802
|
+
content: resolveContent,
|
|
3803
|
+
resolvesFeedbackId: feedbackId
|
|
3804
|
+
}, actorId);
|
|
2979
3805
|
}
|
|
2980
3806
|
/**
|
|
2981
|
-
* [EARS-
|
|
3807
|
+
* [EARS-13, EARS-14] Gets a specific FeedbackRecord by its ID for query.
|
|
2982
3808
|
*
|
|
2983
3809
|
* Description: Gets a specific FeedbackRecord by its ID for query.
|
|
2984
3810
|
* Implementation: Direct read from record store without modifications.
|
|
@@ -2990,11 +3816,11 @@ var FeedbackAdapter = class {
|
|
|
2990
3816
|
return record ? record.payload : null;
|
|
2991
3817
|
}
|
|
2992
3818
|
/**
|
|
2993
|
-
* [EARS-
|
|
3819
|
+
* [EARS-15] Gets all FeedbackRecords associated with a specific entity.
|
|
2994
3820
|
*
|
|
2995
3821
|
* Description: Gets all FeedbackRecords associated with a specific entity.
|
|
2996
3822
|
* Implementation: Reads all records and filters by matching entityId.
|
|
2997
|
-
* Usage: Invoked by `gitgov feedback list` to display feedback for a task/cycle.
|
|
3823
|
+
* Usage: Invoked by `gitgov feedback list` to display feedback for a task/cycle/execution.
|
|
2998
3824
|
* Returns: Array of FeedbackRecords filtered for the entity.
|
|
2999
3825
|
*/
|
|
3000
3826
|
async getFeedbackByEntity(entityId) {
|
|
@@ -3009,11 +3835,11 @@ var FeedbackAdapter = class {
|
|
|
3009
3835
|
return feedbacks;
|
|
3010
3836
|
}
|
|
3011
3837
|
/**
|
|
3012
|
-
* [EARS-
|
|
3838
|
+
* [EARS-16] Gets all FeedbackRecords in the system for indexation.
|
|
3013
3839
|
*
|
|
3014
3840
|
* Description: Gets all FeedbackRecords in the system for complete indexation.
|
|
3015
3841
|
* Implementation: Complete read from record store without filters.
|
|
3016
|
-
* Usage: Invoked by `gitgov feedback list
|
|
3842
|
+
* Usage: Invoked by `gitgov feedback list` and by MetricsAdapter for calculations.
|
|
3017
3843
|
* Returns: Complete array of all FeedbackRecords.
|
|
3018
3844
|
*/
|
|
3019
3845
|
async getAllFeedback() {
|
|
@@ -3027,6 +3853,49 @@ var FeedbackAdapter = class {
|
|
|
3027
3853
|
}
|
|
3028
3854
|
return feedbacks;
|
|
3029
3855
|
}
|
|
3856
|
+
/**
|
|
3857
|
+
* [EARS-17, EARS-18, EARS-19, EARS-20] Builds the complete conversation tree for a feedback.
|
|
3858
|
+
*
|
|
3859
|
+
* Description: Recursively constructs the conversation tree for a feedback.
|
|
3860
|
+
* Implementation: Reads root feedback, finds all responses, builds tree recursively until maxDepth.
|
|
3861
|
+
* Usage: Invoked by `gitgov feedback thread` and `gitgov feedback show --thread`.
|
|
3862
|
+
* Returns: FeedbackThread object with tree structure.
|
|
3863
|
+
*/
|
|
3864
|
+
async getFeedbackThread(feedbackId, maxDepth = Infinity) {
|
|
3865
|
+
return await this.buildThread(feedbackId, maxDepth, 0);
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Private helper: Recursively builds conversation thread.
|
|
3869
|
+
*/
|
|
3870
|
+
async buildThread(feedbackId, maxDepth, currentDepth) {
|
|
3871
|
+
if (currentDepth >= maxDepth) {
|
|
3872
|
+
throw new Error(`Max depth ${maxDepth} reached for feedback thread`);
|
|
3873
|
+
}
|
|
3874
|
+
const feedback = await this.getFeedback(feedbackId);
|
|
3875
|
+
if (!feedback) {
|
|
3876
|
+
throw new Error(`RecordNotFoundError: Feedback not found: ${feedbackId}`);
|
|
3877
|
+
}
|
|
3878
|
+
const allFeedbacks = await this.getAllFeedback();
|
|
3879
|
+
const responses = allFeedbacks.filter(
|
|
3880
|
+
(f) => f.entityType === "feedback" && f.entityId === feedbackId
|
|
3881
|
+
);
|
|
3882
|
+
const responseThreads = [];
|
|
3883
|
+
for (const response of responses) {
|
|
3884
|
+
try {
|
|
3885
|
+
const thread = await this.buildThread(response.id, maxDepth, currentDepth + 1);
|
|
3886
|
+
responseThreads.push(thread);
|
|
3887
|
+
} catch (error) {
|
|
3888
|
+
if (error instanceof Error && error.message.includes("Max depth")) {
|
|
3889
|
+
continue;
|
|
3890
|
+
}
|
|
3891
|
+
throw error;
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
return {
|
|
3895
|
+
feedback,
|
|
3896
|
+
responses: responseThreads
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3030
3899
|
};
|
|
3031
3900
|
|
|
3032
3901
|
// src/adapters/execution_adapter/index.ts
|
|
@@ -3117,16 +3986,7 @@ var ExecutionAdapter = class {
|
|
|
3117
3986
|
* Returns: Complete and signed ExecutionRecord.
|
|
3118
3987
|
*/
|
|
3119
3988
|
async create(payload, actorId) {
|
|
3120
|
-
if (
|
|
3121
|
-
throw new Error("DetailedValidationError: taskId is required");
|
|
3122
|
-
}
|
|
3123
|
-
if (!payload.result) {
|
|
3124
|
-
throw new Error("DetailedValidationError: result is required");
|
|
3125
|
-
}
|
|
3126
|
-
if (payload.result && payload.result.length < 10) {
|
|
3127
|
-
throw new Error("DetailedValidationError: result must be at least 10 characters");
|
|
3128
|
-
}
|
|
3129
|
-
if (this.taskStore) {
|
|
3989
|
+
if (this.taskStore && payload.taskId) {
|
|
3130
3990
|
const taskExists = await this.taskStore.read(payload.taskId);
|
|
3131
3991
|
if (!taskExists) {
|
|
3132
3992
|
throw new Error(`RecordNotFoundError: Task not found: ${payload.taskId}`);
|
|
@@ -3142,9 +4002,9 @@ var ExecutionAdapter = class {
|
|
|
3142
4002
|
signatures: [{
|
|
3143
4003
|
keyId: actorId,
|
|
3144
4004
|
role: "author",
|
|
4005
|
+
notes: "Execution recorded",
|
|
3145
4006
|
signature: "placeholder",
|
|
3146
|
-
timestamp: Date.now()
|
|
3147
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
4007
|
+
timestamp: Date.now()
|
|
3148
4008
|
}]
|
|
3149
4009
|
},
|
|
3150
4010
|
payload: validatedPayload
|
|
@@ -3164,9 +4024,6 @@ var ExecutionAdapter = class {
|
|
|
3164
4024
|
});
|
|
3165
4025
|
return validatedPayload;
|
|
3166
4026
|
} catch (error) {
|
|
3167
|
-
if (error instanceof Error && error.message.includes("DetailedValidationError")) {
|
|
3168
|
-
throw error;
|
|
3169
|
-
}
|
|
3170
4027
|
throw error;
|
|
3171
4028
|
}
|
|
3172
4029
|
}
|
|
@@ -3280,31 +4137,21 @@ async function validateFullChangelogRecord(record, getPublicKey) {
|
|
|
3280
4137
|
// src/factories/changelog_factory.ts
|
|
3281
4138
|
async function createChangelogRecord(payload) {
|
|
3282
4139
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
3283
|
-
let id = payload.id;
|
|
3284
|
-
if (!id && payload.entityType && payload.entityId) {
|
|
3285
|
-
id = generateChangelogId(payload.entityType, payload.entityId, timestamp);
|
|
3286
|
-
}
|
|
3287
4140
|
const changelog = {
|
|
3288
4141
|
// Required fields
|
|
3289
|
-
id: id || "",
|
|
3290
|
-
entityType: payload.entityType || "task",
|
|
3291
|
-
entityId: payload.entityId || "",
|
|
3292
|
-
changeType: payload.changeType || "completion",
|
|
4142
|
+
id: payload.id || "",
|
|
3293
4143
|
title: payload.title || "",
|
|
3294
4144
|
description: payload.description || "",
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
...payload.
|
|
3302
|
-
...payload.usersAffected !== void 0 && { usersAffected: payload.usersAffected },
|
|
3303
|
-
...payload.downtime !== void 0 && { downtime: payload.downtime },
|
|
3304
|
-
...payload.files && { files: payload.files },
|
|
4145
|
+
relatedTasks: payload.relatedTasks || [],
|
|
4146
|
+
completedAt: payload.completedAt || timestamp,
|
|
4147
|
+
// Optional fields (only include if provided)
|
|
4148
|
+
...payload.relatedCycles && { relatedCycles: payload.relatedCycles },
|
|
4149
|
+
...payload.relatedExecutions && { relatedExecutions: payload.relatedExecutions },
|
|
4150
|
+
...payload.version && { version: payload.version },
|
|
4151
|
+
...payload.tags && { tags: payload.tags },
|
|
3305
4152
|
...payload.commits && { commits: payload.commits },
|
|
3306
|
-
...payload.
|
|
3307
|
-
...payload.
|
|
4153
|
+
...payload.files && { files: payload.files },
|
|
4154
|
+
...payload.notes && { notes: payload.notes }
|
|
3308
4155
|
};
|
|
3309
4156
|
const validation = validateChangelogRecordDetailed(changelog);
|
|
3310
4157
|
if (!validation.isValid) {
|
|
@@ -3328,54 +4175,44 @@ var ChangelogAdapter = class {
|
|
|
3328
4175
|
this.cycleStore = dependencies.cycleStore;
|
|
3329
4176
|
}
|
|
3330
4177
|
/**
|
|
3331
|
-
* [EARS-1] Records a
|
|
4178
|
+
* [EARS-1] Records a deliverable/release note.
|
|
3332
4179
|
*
|
|
3333
|
-
* Description:
|
|
3334
|
-
* Implementation: Validates
|
|
3335
|
-
* Usage: Invoked by `gitgov changelog add` to document
|
|
3336
|
-
* Returns: Complete and signed ChangelogRecord
|
|
4180
|
+
* Description: Aggregates multiple tasks into a single deliverable/release note.
|
|
4181
|
+
* Implementation: Validates required fields, builds record with factory, signs, persists and emits event.
|
|
4182
|
+
* Usage: Invoked by `gitgov changelog add` to document deliverables.
|
|
4183
|
+
* Returns: Complete and signed ChangelogRecord.
|
|
3337
4184
|
*/
|
|
3338
4185
|
async create(payload, actorId) {
|
|
3339
|
-
if (!payload.
|
|
3340
|
-
throw new Error("DetailedValidationError:
|
|
3341
|
-
}
|
|
3342
|
-
if (!payload.entityId) {
|
|
3343
|
-
throw new Error("DetailedValidationError: entityId is required");
|
|
3344
|
-
}
|
|
3345
|
-
if (!["task", "cycle", "agent", "system", "configuration"].includes(payload.entityType)) {
|
|
3346
|
-
throw new Error("DetailedValidationError: entityType must be task, cycle, agent, system, or configuration");
|
|
3347
|
-
}
|
|
3348
|
-
if (payload.changeType && !["creation", "completion", "update", "deletion", "hotfix"].includes(payload.changeType)) {
|
|
3349
|
-
throw new Error("DetailedValidationError: changeType must be creation, completion, update, deletion, or hotfix");
|
|
4186
|
+
if (!payload.title || payload.title.length < 10) {
|
|
4187
|
+
throw new Error("DetailedValidationError: title is required and must be at least 10 characters");
|
|
3350
4188
|
}
|
|
3351
|
-
if (payload.
|
|
3352
|
-
throw new Error("DetailedValidationError:
|
|
4189
|
+
if (!payload.description || payload.description.length < 20) {
|
|
4190
|
+
throw new Error("DetailedValidationError: description is required and must be at least 20 characters");
|
|
3353
4191
|
}
|
|
3354
|
-
if (payload.
|
|
3355
|
-
throw new Error("DetailedValidationError:
|
|
4192
|
+
if (!payload.relatedTasks || payload.relatedTasks.length === 0) {
|
|
4193
|
+
throw new Error("DetailedValidationError: relatedTasks is required and must contain at least one task ID");
|
|
3356
4194
|
}
|
|
3357
|
-
if (
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
if (payload.changeType === "completion" && (!payload.references?.tasks || payload.references.tasks.length === 0)) {
|
|
3364
|
-
throw new Error("DetailedValidationError: references.tasks is required when changeType is completion");
|
|
3365
|
-
}
|
|
3366
|
-
if (payload.entityType === "task" && this.taskStore) {
|
|
3367
|
-
const taskExists = await this.taskStore.read(payload.entityId);
|
|
3368
|
-
if (!taskExists) {
|
|
3369
|
-
throw new Error(`RecordNotFoundError: Task not found: ${payload.entityId}`);
|
|
4195
|
+
if (this.taskStore && payload.relatedTasks) {
|
|
4196
|
+
for (const taskId of payload.relatedTasks) {
|
|
4197
|
+
const taskExists = await this.taskStore.read(taskId);
|
|
4198
|
+
if (!taskExists) {
|
|
4199
|
+
throw new Error(`RecordNotFoundError: Task not found: ${taskId}`);
|
|
4200
|
+
}
|
|
3370
4201
|
}
|
|
3371
4202
|
}
|
|
3372
|
-
if (
|
|
3373
|
-
const
|
|
3374
|
-
|
|
3375
|
-
|
|
4203
|
+
if (this.cycleStore && payload.relatedCycles) {
|
|
4204
|
+
for (const cycleId of payload.relatedCycles) {
|
|
4205
|
+
const cycleExists = await this.cycleStore.read(cycleId);
|
|
4206
|
+
if (!cycleExists) {
|
|
4207
|
+
throw new Error(`RecordNotFoundError: Cycle not found: ${cycleId}`);
|
|
4208
|
+
}
|
|
3376
4209
|
}
|
|
3377
4210
|
}
|
|
3378
4211
|
try {
|
|
4212
|
+
const timestamp = payload.completedAt || Math.floor(Date.now() / 1e3);
|
|
4213
|
+
if (!payload.id) {
|
|
4214
|
+
payload.id = generateChangelogId(payload.title, timestamp);
|
|
4215
|
+
}
|
|
3379
4216
|
const validatedPayload = await createChangelogRecord(payload);
|
|
3380
4217
|
const unsignedRecord = {
|
|
3381
4218
|
header: {
|
|
@@ -3385,9 +4222,9 @@ var ChangelogAdapter = class {
|
|
|
3385
4222
|
signatures: [{
|
|
3386
4223
|
keyId: actorId,
|
|
3387
4224
|
role: "author",
|
|
4225
|
+
notes: "Changelog entry created",
|
|
3388
4226
|
signature: "placeholder",
|
|
3389
|
-
timestamp: Date.now()
|
|
3390
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
4227
|
+
timestamp: Date.now()
|
|
3391
4228
|
}]
|
|
3392
4229
|
},
|
|
3393
4230
|
payload: validatedPayload
|
|
@@ -3400,13 +4237,9 @@ var ChangelogAdapter = class {
|
|
|
3400
4237
|
source: "changelog_adapter",
|
|
3401
4238
|
payload: {
|
|
3402
4239
|
changelogId: validatedPayload.id,
|
|
3403
|
-
|
|
3404
|
-
entityType: validatedPayload.entityType,
|
|
3405
|
-
changeType: validatedPayload.changeType,
|
|
3406
|
-
triggeredBy: actorId,
|
|
3407
|
-
riskLevel: validatedPayload.riskLevel,
|
|
4240
|
+
relatedTasks: validatedPayload.relatedTasks,
|
|
3408
4241
|
title: validatedPayload.title,
|
|
3409
|
-
|
|
4242
|
+
version: validatedPayload.version
|
|
3410
4243
|
}
|
|
3411
4244
|
});
|
|
3412
4245
|
return validatedPayload;
|
|
@@ -3418,70 +4251,78 @@ var ChangelogAdapter = class {
|
|
|
3418
4251
|
}
|
|
3419
4252
|
}
|
|
3420
4253
|
/**
|
|
3421
|
-
* [EARS-9] Gets a specific ChangelogRecord by its ID
|
|
3422
|
-
*
|
|
3423
|
-
* Description: Gets a specific ChangelogRecord by its ID for historical query.
|
|
3424
|
-
* Implementation: Direct read from record store without modifications.
|
|
3425
|
-
* Usage: Invoked by `gitgov changelog show` to display change details.
|
|
3426
|
-
* Returns: ChangelogRecord found or null if it doesn't exist.
|
|
4254
|
+
* [EARS-9] Gets a specific ChangelogRecord by its ID.
|
|
3427
4255
|
*/
|
|
3428
4256
|
async getChangelog(changelogId) {
|
|
3429
4257
|
const record = await this.changelogStore.read(changelogId);
|
|
3430
4258
|
return record ? record.payload : null;
|
|
3431
4259
|
}
|
|
3432
4260
|
/**
|
|
3433
|
-
* [EARS-11] Gets all ChangelogRecords
|
|
3434
|
-
*
|
|
3435
|
-
* Description: Gets all ChangelogRecords associated with a specific entity with optional type filtering.
|
|
3436
|
-
* Implementation: Reads all records and filters by matching entityId, optionally by entityType.
|
|
3437
|
-
* Usage: Invoked by `gitgov changelog list` to display history for any system entity.
|
|
3438
|
-
* Returns: Array of ChangelogRecords filtered for the entity.
|
|
4261
|
+
* [EARS-11] Gets all ChangelogRecords that include a specific task.
|
|
3439
4262
|
*/
|
|
3440
|
-
async
|
|
4263
|
+
async getChangelogsByTask(taskId) {
|
|
3441
4264
|
const ids = await this.changelogStore.list();
|
|
3442
4265
|
const changelogs = [];
|
|
3443
4266
|
for (const id of ids) {
|
|
3444
4267
|
const record = await this.changelogStore.read(id);
|
|
3445
|
-
if (record && record.payload.
|
|
3446
|
-
|
|
3447
|
-
changelogs.push(record.payload);
|
|
3448
|
-
}
|
|
4268
|
+
if (record && record.payload.relatedTasks.includes(taskId)) {
|
|
4269
|
+
changelogs.push(record.payload);
|
|
3449
4270
|
}
|
|
3450
4271
|
}
|
|
3451
4272
|
return changelogs;
|
|
3452
4273
|
}
|
|
3453
4274
|
/**
|
|
3454
|
-
* [EARS-12] Gets all ChangelogRecords
|
|
3455
|
-
*
|
|
3456
|
-
* Description: Gets all ChangelogRecords in the system for complete indexation.
|
|
3457
|
-
* Implementation: Complete read from record store without filters.
|
|
3458
|
-
* Usage: Invoked by `gitgov changelog list --all` and MetricsAdapter for activity analysis.
|
|
3459
|
-
* Returns: Complete array of all ChangelogRecords.
|
|
4275
|
+
* [EARS-11, EARS-12, EARS-13] Gets all ChangelogRecords with optional filtering and sorting.
|
|
3460
4276
|
*/
|
|
3461
|
-
async getAllChangelogs() {
|
|
4277
|
+
async getAllChangelogs(options) {
|
|
3462
4278
|
const ids = await this.changelogStore.list();
|
|
3463
|
-
|
|
4279
|
+
let changelogs = [];
|
|
3464
4280
|
for (const id of ids) {
|
|
3465
4281
|
const record = await this.changelogStore.read(id);
|
|
3466
4282
|
if (record) {
|
|
3467
4283
|
changelogs.push(record.payload);
|
|
3468
4284
|
}
|
|
3469
4285
|
}
|
|
4286
|
+
if (options?.tags && options.tags.length > 0) {
|
|
4287
|
+
changelogs = changelogs.filter((changelog) => {
|
|
4288
|
+
if (!changelog.tags) return false;
|
|
4289
|
+
return options.tags.some((tag) => changelog.tags.includes(tag));
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4292
|
+
if (options?.version) {
|
|
4293
|
+
changelogs = changelogs.filter((changelog) => changelog.version === options.version);
|
|
4294
|
+
}
|
|
4295
|
+
const sortBy = options?.sortBy || "completedAt";
|
|
4296
|
+
const sortOrder = options?.sortOrder || "desc";
|
|
4297
|
+
changelogs.sort((a, b) => {
|
|
4298
|
+
let compareValue = 0;
|
|
4299
|
+
if (sortBy === "completedAt") {
|
|
4300
|
+
compareValue = a.completedAt - b.completedAt;
|
|
4301
|
+
} else if (sortBy === "title") {
|
|
4302
|
+
compareValue = a.title.localeCompare(b.title);
|
|
4303
|
+
}
|
|
4304
|
+
return sortOrder === "asc" ? compareValue : -compareValue;
|
|
4305
|
+
});
|
|
4306
|
+
if (options?.limit && options.limit > 0) {
|
|
4307
|
+
changelogs = changelogs.slice(0, options.limit);
|
|
4308
|
+
}
|
|
3470
4309
|
return changelogs;
|
|
3471
4310
|
}
|
|
3472
4311
|
/**
|
|
3473
|
-
* [EARS-13] Gets recent ChangelogRecords ordered by
|
|
3474
|
-
*
|
|
3475
|
-
* Description: Gets recent ChangelogRecords ordered by timestamp for dashboard and monitoring.
|
|
3476
|
-
* Implementation: Reads all records, sorts by timestamp descending and applies limit.
|
|
3477
|
-
* Usage: Invoked by `gitgov changelog list --recent` and dashboard for activity monitoring.
|
|
3478
|
-
* Returns: Array of ChangelogRecords limited and ordered by timestamp.
|
|
4312
|
+
* [EARS-13] Gets recent ChangelogRecords ordered by completedAt.
|
|
3479
4313
|
*/
|
|
3480
4314
|
async getRecentChangelogs(limit) {
|
|
3481
4315
|
const allChangelogs = await this.getAllChangelogs();
|
|
3482
|
-
const sortedChangelogs = allChangelogs.sort((a, b) => b.
|
|
4316
|
+
const sortedChangelogs = allChangelogs.sort((a, b) => b.completedAt - a.completedAt);
|
|
3483
4317
|
return sortedChangelogs.slice(0, limit);
|
|
3484
4318
|
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Legacy method for backwards compatibility - maps to getChangelogsByTask
|
|
4321
|
+
* @deprecated Use getChangelogsByTask instead
|
|
4322
|
+
*/
|
|
4323
|
+
async getChangelogsByEntity(entityId, _entityType) {
|
|
4324
|
+
return this.getChangelogsByTask(entityId);
|
|
4325
|
+
}
|
|
3485
4326
|
};
|
|
3486
4327
|
|
|
3487
4328
|
// src/adapters/metrics_adapter/index.ts
|
|
@@ -3555,13 +4396,17 @@ var MetricsAdapter = class {
|
|
|
3555
4396
|
}
|
|
3556
4397
|
const task = taskRecord.payload;
|
|
3557
4398
|
let feedbacks = [];
|
|
4399
|
+
let allFeedbacks = [];
|
|
3558
4400
|
let executions = [];
|
|
3559
4401
|
if (this.feedbackStore) {
|
|
3560
4402
|
const feedbackIds = await this.feedbackStore.list();
|
|
3561
4403
|
for (const id of feedbackIds) {
|
|
3562
4404
|
const record = await this.feedbackStore.read(id);
|
|
3563
|
-
if (record
|
|
3564
|
-
|
|
4405
|
+
if (record) {
|
|
4406
|
+
allFeedbacks.push(record.payload);
|
|
4407
|
+
if (record.payload.entityId === taskId) {
|
|
4408
|
+
feedbacks.push(record.payload);
|
|
4409
|
+
}
|
|
3565
4410
|
}
|
|
3566
4411
|
}
|
|
3567
4412
|
}
|
|
@@ -3576,7 +4421,13 @@ var MetricsAdapter = class {
|
|
|
3576
4421
|
}
|
|
3577
4422
|
const timeInCurrentStage = this.calculateTimeInCurrentStage(task);
|
|
3578
4423
|
const stalenessIndex = this.calculateStalenessIndex([task]);
|
|
3579
|
-
const blockingFeedbacks = feedbacks.filter((f) =>
|
|
4424
|
+
const blockingFeedbacks = feedbacks.filter((f) => {
|
|
4425
|
+
if (f.type !== "blocking" || f.status !== "open") return false;
|
|
4426
|
+
const hasResolution = allFeedbacks.some(
|
|
4427
|
+
(resolution) => resolution.entityType === "feedback" && resolution.resolvesFeedbackId === f.id && resolution.status === "resolved"
|
|
4428
|
+
);
|
|
4429
|
+
return !hasResolution;
|
|
4430
|
+
}).length;
|
|
3580
4431
|
const lastActivity = executions.length > 0 ? Math.max(...executions.map((e) => this.getTimestampFromId(e.id))) : this.getTimestampFromId(task.id);
|
|
3581
4432
|
const recommendations = [];
|
|
3582
4433
|
if (timeInCurrentStage > 7) recommendations.push("Task has been stagnant for over 7 days");
|
|
@@ -4034,10 +4885,6 @@ var BacklogAdapter = class {
|
|
|
4034
4885
|
"feedback.created",
|
|
4035
4886
|
(event) => this.handleFeedbackCreated(event)
|
|
4036
4887
|
);
|
|
4037
|
-
this.eventBus.subscribe(
|
|
4038
|
-
"feedback.status.changed",
|
|
4039
|
-
(event) => this.handleFeedbackResolved(event)
|
|
4040
|
-
);
|
|
4041
4888
|
this.eventBus.subscribe(
|
|
4042
4889
|
"execution.created",
|
|
4043
4890
|
(event) => this.handleExecutionCreated(event)
|
|
@@ -4069,9 +4916,9 @@ var BacklogAdapter = class {
|
|
|
4069
4916
|
signatures: [{
|
|
4070
4917
|
keyId: actorId,
|
|
4071
4918
|
role: "author",
|
|
4919
|
+
notes: "Task created",
|
|
4072
4920
|
signature: "placeholder",
|
|
4073
|
-
timestamp: Date.now()
|
|
4074
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
4921
|
+
timestamp: Date.now()
|
|
4075
4922
|
}]
|
|
4076
4923
|
},
|
|
4077
4924
|
payload: validatedPayload
|
|
@@ -4174,9 +5021,9 @@ var BacklogAdapter = class {
|
|
|
4174
5021
|
const tempSignature = {
|
|
4175
5022
|
keyId: actorId,
|
|
4176
5023
|
role: "approver",
|
|
5024
|
+
notes: "Task approval",
|
|
4177
5025
|
signature: "temp-signature",
|
|
4178
|
-
timestamp: Date.now()
|
|
4179
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
5026
|
+
timestamp: Date.now()
|
|
4180
5027
|
};
|
|
4181
5028
|
const context = {
|
|
4182
5029
|
task,
|
|
@@ -4507,7 +5354,16 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4507
5354
|
}
|
|
4508
5355
|
// ===== PHASE 3: EVENT HANDLERS (NEW IMPLEMENTATION) =====
|
|
4509
5356
|
/**
|
|
4510
|
-
* [EARS-31] Handles feedback created events
|
|
5357
|
+
* [EARS-31, EARS-33, EARS-34] Handles feedback created events (Immutable Pattern)
|
|
5358
|
+
*
|
|
5359
|
+
* This handler respects the immutable feedback pattern:
|
|
5360
|
+
* - Case 1: Blocking feedback created → pause task if active/ready
|
|
5361
|
+
* - Case 2: Feedback resolving another feedback → resume task if no more blocks
|
|
5362
|
+
*
|
|
5363
|
+
* The immutable pattern means:
|
|
5364
|
+
* - Original feedbacks NEVER change status
|
|
5365
|
+
* - Resolution is expressed by creating a NEW feedback pointing to the original
|
|
5366
|
+
* - We detect resolution via: entityType='feedback' + status='resolved' + resolvesFeedbackId
|
|
4511
5367
|
*/
|
|
4512
5368
|
async handleFeedbackCreated(event) {
|
|
4513
5369
|
try {
|
|
@@ -4517,85 +5373,72 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4517
5373
|
processedAt: Date.now(),
|
|
4518
5374
|
sourceAdapter: "backlog_adapter"
|
|
4519
5375
|
};
|
|
4520
|
-
if (event.payload.type
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
5376
|
+
if (event.payload.type === "blocking" && event.payload.entityType === "task") {
|
|
5377
|
+
const task = await this.getTask(event.payload.entityId);
|
|
5378
|
+
if (!task) {
|
|
5379
|
+
console.warn(`Task not found for feedback: ${event.payload.entityId}`);
|
|
5380
|
+
return;
|
|
5381
|
+
}
|
|
5382
|
+
if (!["active", "ready"].includes(task.status)) {
|
|
5383
|
+
return;
|
|
5384
|
+
}
|
|
5385
|
+
const updatedTask = { ...task, status: "paused" };
|
|
5386
|
+
const taskRecord = await this.taskStore.read(task.id);
|
|
5387
|
+
if (taskRecord) {
|
|
5388
|
+
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
5389
|
+
await this.taskStore.write(updatedRecord);
|
|
5390
|
+
this.eventBus.publish({
|
|
5391
|
+
type: "task.status.changed",
|
|
5392
|
+
timestamp: Date.now(),
|
|
5393
|
+
source: "backlog_adapter",
|
|
5394
|
+
payload: {
|
|
5395
|
+
taskId: task.id,
|
|
5396
|
+
oldStatus: task.status,
|
|
5397
|
+
newStatus: "paused",
|
|
5398
|
+
actorId: "system"
|
|
5399
|
+
},
|
|
5400
|
+
metadata
|
|
5401
|
+
});
|
|
5402
|
+
}
|
|
4531
5403
|
return;
|
|
4532
5404
|
}
|
|
4533
|
-
if (
|
|
5405
|
+
if (event.payload.entityType === "feedback" && event.payload.status === "resolved" && event.payload.resolvesFeedbackId) {
|
|
5406
|
+
const originalFeedback = await this.feedbackAdapter.getFeedback(event.payload.resolvesFeedbackId);
|
|
5407
|
+
if (!originalFeedback || originalFeedback.type !== "blocking") {
|
|
5408
|
+
return;
|
|
5409
|
+
}
|
|
5410
|
+
const task = await this.getTask(originalFeedback.entityId);
|
|
5411
|
+
if (!task || task.status !== "paused") {
|
|
5412
|
+
return;
|
|
5413
|
+
}
|
|
5414
|
+
const taskHealth = await this.metricsAdapter.getTaskHealth(task.id);
|
|
5415
|
+
if (taskHealth.blockingFeedbacks > 0) {
|
|
5416
|
+
return;
|
|
5417
|
+
}
|
|
5418
|
+
const updatedTask = { ...task, status: "active" };
|
|
5419
|
+
const taskRecord = await this.taskStore.read(task.id);
|
|
5420
|
+
if (taskRecord) {
|
|
5421
|
+
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
5422
|
+
await this.taskStore.write(updatedRecord);
|
|
5423
|
+
this.eventBus.publish({
|
|
5424
|
+
type: "task.status.changed",
|
|
5425
|
+
timestamp: Date.now(),
|
|
5426
|
+
source: "backlog_adapter",
|
|
5427
|
+
payload: {
|
|
5428
|
+
taskId: task.id,
|
|
5429
|
+
oldStatus: "paused",
|
|
5430
|
+
newStatus: "active",
|
|
5431
|
+
actorId: "system"
|
|
5432
|
+
},
|
|
5433
|
+
metadata
|
|
5434
|
+
});
|
|
5435
|
+
}
|
|
4534
5436
|
return;
|
|
4535
5437
|
}
|
|
4536
|
-
const updatedTask = { ...task, status: "paused" };
|
|
4537
|
-
const taskRecord = await this.taskStore.read(task.id);
|
|
4538
|
-
if (taskRecord) {
|
|
4539
|
-
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
4540
|
-
await this.taskStore.write(updatedRecord);
|
|
4541
|
-
this.eventBus.publish({
|
|
4542
|
-
type: "task.status.changed",
|
|
4543
|
-
timestamp: Date.now(),
|
|
4544
|
-
source: "backlog_adapter",
|
|
4545
|
-
payload: {
|
|
4546
|
-
taskId: task.id,
|
|
4547
|
-
oldStatus: task.status,
|
|
4548
|
-
newStatus: "paused",
|
|
4549
|
-
actorId: "system"
|
|
4550
|
-
},
|
|
4551
|
-
metadata
|
|
4552
|
-
});
|
|
4553
|
-
}
|
|
4554
5438
|
} catch (error) {
|
|
4555
5439
|
console.error("Error in handleFeedbackCreated:", error);
|
|
4556
5440
|
}
|
|
4557
5441
|
}
|
|
4558
|
-
/**
|
|
4559
|
-
* [EARS-33] Handles feedback resolved events - resumes task if no more blocks
|
|
4560
|
-
*/
|
|
4561
|
-
async handleFeedbackResolved(event) {
|
|
4562
|
-
try {
|
|
4563
|
-
if (event.payload.oldStatus !== "open" || event.payload.newStatus !== "resolved") {
|
|
4564
|
-
return;
|
|
4565
|
-
}
|
|
4566
|
-
const feedbackRecord = await this.feedbackStore.read(event.payload.feedbackId);
|
|
4567
|
-
if (!feedbackRecord) {
|
|
4568
|
-
return;
|
|
4569
|
-
}
|
|
4570
|
-
const task = await this.getTask(feedbackRecord.payload.entityId);
|
|
4571
|
-
if (!task || task.status !== "paused") {
|
|
4572
|
-
return;
|
|
4573
|
-
}
|
|
4574
|
-
const taskHealth = await this.metricsAdapter.getTaskHealth(task.id);
|
|
4575
|
-
if (taskHealth.blockingFeedbacks > 0) {
|
|
4576
|
-
return;
|
|
4577
|
-
}
|
|
4578
|
-
const updatedTask = { ...task, status: "active" };
|
|
4579
|
-
const taskRecord = await this.taskStore.read(task.id);
|
|
4580
|
-
if (taskRecord) {
|
|
4581
|
-
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
4582
|
-
await this.taskStore.write(updatedRecord);
|
|
4583
|
-
this.eventBus.publish({
|
|
4584
|
-
type: "task.status.changed",
|
|
4585
|
-
timestamp: Date.now(),
|
|
4586
|
-
source: "backlog_adapter",
|
|
4587
|
-
payload: {
|
|
4588
|
-
taskId: task.id,
|
|
4589
|
-
oldStatus: "paused",
|
|
4590
|
-
newStatus: "active",
|
|
4591
|
-
actorId: "system"
|
|
4592
|
-
}
|
|
4593
|
-
});
|
|
4594
|
-
}
|
|
4595
|
-
} catch (error) {
|
|
4596
|
-
console.error("Error in handleFeedbackResolved:", error);
|
|
4597
|
-
}
|
|
4598
|
-
}
|
|
4599
5442
|
/**
|
|
4600
5443
|
* [EARS-35] Handles execution created events - transitions ready→active on first execution
|
|
4601
5444
|
*/
|
|
@@ -4649,29 +5492,31 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4649
5492
|
console.warn(`Changelog not found: ${event.payload.changelogId}`);
|
|
4650
5493
|
return;
|
|
4651
5494
|
}
|
|
4652
|
-
if (changelogRecord.payload.
|
|
4653
|
-
return;
|
|
4654
|
-
}
|
|
4655
|
-
const task = await this.getTask(changelogRecord.payload.entityId);
|
|
4656
|
-
if (!task || task.status !== "done") {
|
|
5495
|
+
if (!changelogRecord.payload.relatedTasks || changelogRecord.payload.relatedTasks.length === 0) {
|
|
4657
5496
|
return;
|
|
4658
5497
|
}
|
|
4659
|
-
const
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
5498
|
+
for (const taskId of changelogRecord.payload.relatedTasks) {
|
|
5499
|
+
const task = await this.getTask(taskId);
|
|
5500
|
+
if (!task || task.status !== "done") {
|
|
5501
|
+
continue;
|
|
5502
|
+
}
|
|
5503
|
+
const updatedTask = { ...task, status: "archived" };
|
|
5504
|
+
const taskRecord = await this.taskStore.read(task.id);
|
|
5505
|
+
if (taskRecord) {
|
|
5506
|
+
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
5507
|
+
await this.taskStore.write(updatedRecord);
|
|
5508
|
+
this.eventBus.publish({
|
|
5509
|
+
type: "task.status.changed",
|
|
5510
|
+
timestamp: Date.now(),
|
|
5511
|
+
source: "backlog_adapter",
|
|
5512
|
+
payload: {
|
|
5513
|
+
taskId: task.id,
|
|
5514
|
+
oldStatus: "done",
|
|
5515
|
+
newStatus: "archived",
|
|
5516
|
+
actorId: "system"
|
|
5517
|
+
}
|
|
5518
|
+
});
|
|
5519
|
+
}
|
|
4675
5520
|
}
|
|
4676
5521
|
} catch (error) {
|
|
4677
5522
|
console.error("Error in handleChangelogCreated:", error);
|
|
@@ -4785,9 +5630,9 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4785
5630
|
signatures: [{
|
|
4786
5631
|
keyId: actorId,
|
|
4787
5632
|
role: "author",
|
|
5633
|
+
notes: "Cycle created",
|
|
4788
5634
|
signature: "placeholder",
|
|
4789
|
-
timestamp: Date.now()
|
|
4790
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
5635
|
+
timestamp: Date.now()
|
|
4791
5636
|
}]
|
|
4792
5637
|
},
|
|
4793
5638
|
payload: validatedPayload
|
|
@@ -5146,11 +5991,14 @@ var FileIndexerAdapter = class {
|
|
|
5146
5991
|
metrics: { ...systemStatus, ...productivityMetrics, ...collaborationMetrics },
|
|
5147
5992
|
activityHistory,
|
|
5148
5993
|
// NUEVO
|
|
5149
|
-
tasks,
|
|
5994
|
+
tasks: tasks.map((t) => t.payload),
|
|
5995
|
+
// Extract payloads for IndexData
|
|
5150
5996
|
enrichedTasks,
|
|
5151
5997
|
// NUEVO - Tasks with activity metadata
|
|
5152
|
-
cycles,
|
|
5153
|
-
|
|
5998
|
+
cycles: cycles.map((c) => c.payload),
|
|
5999
|
+
// Extract payloads for IndexData
|
|
6000
|
+
actors: actors.map((a) => a.payload)
|
|
6001
|
+
// Extract payloads for IndexData
|
|
5154
6002
|
};
|
|
5155
6003
|
const writeStart = performance.now();
|
|
5156
6004
|
await this.writeCacheFile(indexData);
|
|
@@ -5218,19 +6066,19 @@ var FileIndexerAdapter = class {
|
|
|
5218
6066
|
]);
|
|
5219
6067
|
recordsScanned = tasks.length + cycles.length;
|
|
5220
6068
|
for (const task of tasks) {
|
|
5221
|
-
if (!task.id || !task.description) {
|
|
6069
|
+
if (!task.payload.id || !task.payload.description) {
|
|
5222
6070
|
errors.push({
|
|
5223
6071
|
type: "schema_violation",
|
|
5224
|
-
recordId: task.id || "unknown",
|
|
6072
|
+
recordId: task.payload.id || "unknown",
|
|
5225
6073
|
message: "Task missing required fields"
|
|
5226
6074
|
});
|
|
5227
6075
|
}
|
|
5228
6076
|
}
|
|
5229
6077
|
for (const cycle of cycles) {
|
|
5230
|
-
if (!cycle.id || !cycle.title) {
|
|
6078
|
+
if (!cycle.payload.id || !cycle.payload.title) {
|
|
5231
6079
|
errors.push({
|
|
5232
6080
|
type: "schema_violation",
|
|
5233
|
-
recordId: cycle.id || "unknown",
|
|
6081
|
+
recordId: cycle.payload.id || "unknown",
|
|
5234
6082
|
message: "Cycle missing required fields"
|
|
5235
6083
|
});
|
|
5236
6084
|
}
|
|
@@ -5303,7 +6151,8 @@ var FileIndexerAdapter = class {
|
|
|
5303
6151
|
}
|
|
5304
6152
|
// ===== HELPER METHODS =====
|
|
5305
6153
|
/**
|
|
5306
|
-
* Reads all tasks from taskStore
|
|
6154
|
+
* Reads all tasks from taskStore with full metadata (headers + payloads).
|
|
6155
|
+
* Returns complete GitGovTaskRecord objects including signatures for author/lastModifier extraction.
|
|
5307
6156
|
*/
|
|
5308
6157
|
async readAllTasks() {
|
|
5309
6158
|
const taskIds = await this.taskStore.list();
|
|
@@ -5311,13 +6160,13 @@ var FileIndexerAdapter = class {
|
|
|
5311
6160
|
for (const id of taskIds) {
|
|
5312
6161
|
const record = await this.taskStore.read(id);
|
|
5313
6162
|
if (record) {
|
|
5314
|
-
tasks.push(record
|
|
6163
|
+
tasks.push(record);
|
|
5315
6164
|
}
|
|
5316
6165
|
}
|
|
5317
6166
|
return tasks;
|
|
5318
6167
|
}
|
|
5319
6168
|
/**
|
|
5320
|
-
* Reads all cycles from cycleStore
|
|
6169
|
+
* Reads all cycles from cycleStore with full metadata.
|
|
5321
6170
|
*/
|
|
5322
6171
|
async readAllCycles() {
|
|
5323
6172
|
const cycleIds = await this.cycleStore.list();
|
|
@@ -5325,13 +6174,13 @@ var FileIndexerAdapter = class {
|
|
|
5325
6174
|
for (const id of cycleIds) {
|
|
5326
6175
|
const record = await this.cycleStore.read(id);
|
|
5327
6176
|
if (record) {
|
|
5328
|
-
cycles.push(record
|
|
6177
|
+
cycles.push(record);
|
|
5329
6178
|
}
|
|
5330
6179
|
}
|
|
5331
6180
|
return cycles;
|
|
5332
6181
|
}
|
|
5333
6182
|
/**
|
|
5334
|
-
* Reads all actors from actorStore (graceful degradation)
|
|
6183
|
+
* Reads all actors from actorStore (graceful degradation) with full metadata.
|
|
5335
6184
|
*/
|
|
5336
6185
|
async readAllActors() {
|
|
5337
6186
|
if (!this.actorStore) {
|
|
@@ -5342,13 +6191,13 @@ var FileIndexerAdapter = class {
|
|
|
5342
6191
|
for (const id of actorIds) {
|
|
5343
6192
|
const record = await this.actorStore.read(id);
|
|
5344
6193
|
if (record) {
|
|
5345
|
-
actors.push(record
|
|
6194
|
+
actors.push(record);
|
|
5346
6195
|
}
|
|
5347
6196
|
}
|
|
5348
6197
|
return actors;
|
|
5349
6198
|
}
|
|
5350
6199
|
/**
|
|
5351
|
-
* Reads all feedback from feedbackStore (graceful degradation)
|
|
6200
|
+
* Reads all feedback from feedbackStore (graceful degradation) with full metadata.
|
|
5352
6201
|
*/
|
|
5353
6202
|
async readAllFeedback() {
|
|
5354
6203
|
if (!this.feedbackStore) {
|
|
@@ -5359,13 +6208,13 @@ var FileIndexerAdapter = class {
|
|
|
5359
6208
|
for (const id of feedbackIds) {
|
|
5360
6209
|
const record = await this.feedbackStore.read(id);
|
|
5361
6210
|
if (record) {
|
|
5362
|
-
feedback.push(record
|
|
6211
|
+
feedback.push(record);
|
|
5363
6212
|
}
|
|
5364
6213
|
}
|
|
5365
6214
|
return feedback;
|
|
5366
6215
|
}
|
|
5367
6216
|
/**
|
|
5368
|
-
* Reads all executions from executionStore (graceful degradation)
|
|
6217
|
+
* Reads all executions from executionStore (graceful degradation) with full metadata.
|
|
5369
6218
|
*/
|
|
5370
6219
|
async readAllExecutions() {
|
|
5371
6220
|
if (!this.executionStore) {
|
|
@@ -5376,13 +6225,13 @@ var FileIndexerAdapter = class {
|
|
|
5376
6225
|
for (const id of executionIds) {
|
|
5377
6226
|
const record = await this.executionStore.read(id);
|
|
5378
6227
|
if (record) {
|
|
5379
|
-
executions.push(record
|
|
6228
|
+
executions.push(record);
|
|
5380
6229
|
}
|
|
5381
6230
|
}
|
|
5382
6231
|
return executions;
|
|
5383
6232
|
}
|
|
5384
6233
|
/**
|
|
5385
|
-
* Reads all changelogs from changelogStore (graceful degradation)
|
|
6234
|
+
* Reads all changelogs from changelogStore (graceful degradation) with full metadata.
|
|
5386
6235
|
*/
|
|
5387
6236
|
async readAllChangelogs() {
|
|
5388
6237
|
if (!this.changelogStore) {
|
|
@@ -5393,7 +6242,7 @@ var FileIndexerAdapter = class {
|
|
|
5393
6242
|
for (const id of changelogIds) {
|
|
5394
6243
|
const record = await this.changelogStore.read(id);
|
|
5395
6244
|
if (record) {
|
|
5396
|
-
changelogs.push(record
|
|
6245
|
+
changelogs.push(record);
|
|
5397
6246
|
}
|
|
5398
6247
|
}
|
|
5399
6248
|
return changelogs;
|
|
@@ -5446,96 +6295,96 @@ var FileIndexerAdapter = class {
|
|
|
5446
6295
|
const events = [];
|
|
5447
6296
|
try {
|
|
5448
6297
|
allRecords.tasks.forEach((task) => {
|
|
5449
|
-
const timestampPart = task.id.split("-")[0];
|
|
6298
|
+
const timestampPart = task.payload.id.split("-")[0];
|
|
5450
6299
|
if (timestampPart) {
|
|
5451
6300
|
events.push({
|
|
5452
6301
|
timestamp: parseInt(timestampPart),
|
|
5453
6302
|
type: "task_created",
|
|
5454
|
-
entityId: task.id,
|
|
5455
|
-
entityTitle: task.title,
|
|
5456
|
-
actorId: "
|
|
5457
|
-
//
|
|
5458
|
-
metadata: { priority: task.priority, status: task.status }
|
|
6303
|
+
entityId: task.payload.id,
|
|
6304
|
+
entityTitle: task.payload.title,
|
|
6305
|
+
actorId: task.header.signatures[0]?.keyId || "unknown",
|
|
6306
|
+
// Extract from first signature
|
|
6307
|
+
metadata: { priority: task.payload.priority, status: task.payload.status }
|
|
5459
6308
|
});
|
|
5460
6309
|
}
|
|
5461
6310
|
});
|
|
5462
6311
|
allRecords.cycles.forEach((cycle) => {
|
|
5463
|
-
const timestampPart = cycle.id.split("-")[0];
|
|
6312
|
+
const timestampPart = cycle.payload.id.split("-")[0];
|
|
5464
6313
|
if (timestampPart) {
|
|
5465
6314
|
events.push({
|
|
5466
6315
|
timestamp: parseInt(timestampPart),
|
|
5467
6316
|
type: "cycle_created",
|
|
5468
|
-
entityId: cycle.id,
|
|
5469
|
-
entityTitle: cycle.title,
|
|
5470
|
-
actorId: "
|
|
5471
|
-
//
|
|
5472
|
-
metadata: { status: cycle.status }
|
|
6317
|
+
entityId: cycle.payload.id,
|
|
6318
|
+
entityTitle: cycle.payload.title,
|
|
6319
|
+
actorId: cycle.header.signatures[0]?.keyId || "unknown",
|
|
6320
|
+
// Extract from first signature
|
|
6321
|
+
metadata: { status: cycle.payload.status }
|
|
5473
6322
|
});
|
|
5474
6323
|
}
|
|
5475
6324
|
});
|
|
5476
6325
|
allRecords.feedback.forEach((feedback) => {
|
|
5477
|
-
const timestampPart = feedback.id.split("-")[0];
|
|
6326
|
+
const timestampPart = feedback.payload.id.split("-")[0];
|
|
5478
6327
|
if (timestampPart) {
|
|
5479
6328
|
const metadata = {
|
|
5480
|
-
type: feedback.type,
|
|
5481
|
-
resolution: feedback.status
|
|
6329
|
+
type: feedback.payload.type,
|
|
6330
|
+
resolution: feedback.payload.status
|
|
5482
6331
|
};
|
|
5483
|
-
if (feedback.assignee) {
|
|
5484
|
-
metadata.assignee = feedback.assignee;
|
|
6332
|
+
if (feedback.payload.assignee) {
|
|
6333
|
+
metadata.assignee = feedback.payload.assignee;
|
|
5485
6334
|
}
|
|
5486
6335
|
const event = {
|
|
5487
6336
|
timestamp: parseInt(timestampPart),
|
|
5488
6337
|
type: "feedback_created",
|
|
5489
|
-
entityId: feedback.id,
|
|
5490
|
-
entityTitle: `${feedback.type}: ${feedback.content.slice(0, 40)}...`,
|
|
6338
|
+
entityId: feedback.payload.id,
|
|
6339
|
+
entityTitle: `${feedback.payload.type}: ${feedback.payload.content.slice(0, 40)}...`,
|
|
6340
|
+
actorId: feedback.header.signatures[0]?.keyId || feedback.payload.assignee || "unknown",
|
|
5491
6341
|
metadata
|
|
5492
6342
|
};
|
|
5493
|
-
if (feedback.assignee) {
|
|
5494
|
-
event.actorId = feedback.assignee;
|
|
5495
|
-
}
|
|
5496
6343
|
events.push(event);
|
|
5497
6344
|
}
|
|
5498
6345
|
});
|
|
5499
6346
|
allRecords.changelogs.forEach((changelog) => {
|
|
5500
|
-
const timestampPart = changelog.id.split("-")[0];
|
|
6347
|
+
const timestampPart = changelog.payload.id.split("-")[0];
|
|
5501
6348
|
if (timestampPart) {
|
|
5502
|
-
|
|
6349
|
+
const event = {
|
|
5503
6350
|
timestamp: parseInt(timestampPart),
|
|
5504
6351
|
type: "changelog_created",
|
|
5505
|
-
entityId: changelog.id,
|
|
5506
|
-
entityTitle: changelog.title || "Release notes",
|
|
5507
|
-
actorId: "
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
6352
|
+
entityId: changelog.payload.id,
|
|
6353
|
+
entityTitle: changelog.payload.title || "Release notes",
|
|
6354
|
+
actorId: changelog.header.signatures[0]?.keyId || "unknown"
|
|
6355
|
+
};
|
|
6356
|
+
if (changelog.payload.version) {
|
|
6357
|
+
event.metadata = { version: changelog.payload.version };
|
|
6358
|
+
}
|
|
6359
|
+
events.push(event);
|
|
5511
6360
|
}
|
|
5512
6361
|
});
|
|
5513
6362
|
allRecords.executions.forEach((execution) => {
|
|
5514
|
-
const timestampPart = execution.id.split("-")[0];
|
|
6363
|
+
const timestampPart = execution.payload.id.split("-")[0];
|
|
5515
6364
|
if (timestampPart) {
|
|
5516
6365
|
events.push({
|
|
5517
6366
|
timestamp: parseInt(timestampPart),
|
|
5518
6367
|
type: "execution_created",
|
|
5519
|
-
entityId: execution.id,
|
|
5520
|
-
entityTitle: execution.title || `Working on ${execution.taskId.slice(-8)}`,
|
|
5521
|
-
actorId: "
|
|
5522
|
-
//
|
|
6368
|
+
entityId: execution.payload.id,
|
|
6369
|
+
entityTitle: execution.payload.title || `Working on ${execution.payload.taskId.slice(-8)}`,
|
|
6370
|
+
actorId: execution.header.signatures[0]?.keyId || "unknown",
|
|
6371
|
+
// Extract from first signature
|
|
5523
6372
|
metadata: {
|
|
5524
|
-
executionType: execution.type || "development",
|
|
5525
|
-
taskId: execution.taskId
|
|
6373
|
+
executionType: execution.payload.type || "development",
|
|
6374
|
+
taskId: execution.payload.taskId
|
|
5526
6375
|
}
|
|
5527
6376
|
});
|
|
5528
6377
|
}
|
|
5529
6378
|
});
|
|
5530
6379
|
allRecords.actors.forEach((actor) => {
|
|
5531
|
-
const timestampPart = actor.id.split("-")[0];
|
|
6380
|
+
const timestampPart = actor.payload.id.split("-")[0];
|
|
5532
6381
|
if (timestampPart) {
|
|
5533
6382
|
events.push({
|
|
5534
6383
|
timestamp: parseInt(timestampPart),
|
|
5535
6384
|
type: "actor_created",
|
|
5536
|
-
entityId: actor.id,
|
|
5537
|
-
entityTitle: `${actor.displayName} joined (${actor.type})`,
|
|
5538
|
-
metadata: { type: actor.type }
|
|
6385
|
+
entityId: actor.payload.id,
|
|
6386
|
+
entityTitle: `${actor.payload.displayName} joined (${actor.payload.type})`,
|
|
6387
|
+
metadata: { type: actor.payload.type }
|
|
5539
6388
|
});
|
|
5540
6389
|
}
|
|
5541
6390
|
});
|
|
@@ -5572,34 +6421,34 @@ var FileIndexerAdapter = class {
|
|
|
5572
6421
|
} catch (error) {
|
|
5573
6422
|
}
|
|
5574
6423
|
const relatedFeedback = relatedRecords.feedback.filter(
|
|
5575
|
-
(f) => f.entityId === task.id || f.content.includes(task.id)
|
|
6424
|
+
(f) => f.payload.entityId === task.id || f.payload.content.includes(task.id)
|
|
5576
6425
|
);
|
|
5577
6426
|
for (const feedback of relatedFeedback) {
|
|
5578
|
-
const feedbackTime = this.getTimestampFromId(feedback.id) * 1e3;
|
|
6427
|
+
const feedbackTime = this.getTimestampFromId(feedback.payload.id) * 1e3;
|
|
5579
6428
|
if (feedbackTime > lastUpdated) {
|
|
5580
6429
|
lastUpdated = feedbackTime;
|
|
5581
6430
|
lastActivityType = "feedback_received";
|
|
5582
|
-
recentActivity = `${feedback.type} feedback: ${feedback.content.slice(0, 30)}...`;
|
|
6431
|
+
recentActivity = `${feedback.payload.type} feedback: ${feedback.payload.content.slice(0, 30)}...`;
|
|
5583
6432
|
}
|
|
5584
6433
|
}
|
|
5585
|
-
const relatedExecutions = relatedRecords.executions.filter((e) => e.taskId === task.id);
|
|
6434
|
+
const relatedExecutions = relatedRecords.executions.filter((e) => e.payload.taskId === task.id);
|
|
5586
6435
|
for (const execution of relatedExecutions) {
|
|
5587
|
-
const executionTime = this.getTimestampFromId(execution.id) * 1e3;
|
|
6436
|
+
const executionTime = this.getTimestampFromId(execution.payload.id) * 1e3;
|
|
5588
6437
|
if (executionTime > lastUpdated) {
|
|
5589
6438
|
lastUpdated = executionTime;
|
|
5590
6439
|
lastActivityType = "execution_added";
|
|
5591
|
-
recentActivity = `Execution: ${execution.title || "Work logged"}`;
|
|
6440
|
+
recentActivity = `Execution: ${execution.payload.title || "Work logged"}`;
|
|
5592
6441
|
}
|
|
5593
6442
|
}
|
|
5594
6443
|
const relatedChangelogs = relatedRecords.changelogs.filter(
|
|
5595
|
-
(c) => c.
|
|
6444
|
+
(c) => c.payload.relatedTasks.includes(task.id) || c.payload.description?.includes(task.id)
|
|
5596
6445
|
);
|
|
5597
6446
|
for (const changelog of relatedChangelogs) {
|
|
5598
|
-
const changelogTime = this.getTimestampFromId(changelog.id) * 1e3;
|
|
6447
|
+
const changelogTime = this.getTimestampFromId(changelog.payload.id) * 1e3;
|
|
5599
6448
|
if (changelogTime > lastUpdated) {
|
|
5600
6449
|
lastUpdated = changelogTime;
|
|
5601
6450
|
lastActivityType = "changelog_created";
|
|
5602
|
-
recentActivity = `Changelog: ${changelog.title}`;
|
|
6451
|
+
recentActivity = `Changelog: ${changelog.payload.title}`;
|
|
5603
6452
|
}
|
|
5604
6453
|
}
|
|
5605
6454
|
return { lastUpdated, lastActivityType, recentActivity };
|
|
@@ -5614,11 +6463,13 @@ var FileIndexerAdapter = class {
|
|
|
5614
6463
|
}
|
|
5615
6464
|
/**
|
|
5616
6465
|
* [EARS-22] Enrich a TaskRecord with activity metadata
|
|
6466
|
+
* @param task - Full GitGovTaskRecord with header.signatures for author/lastModifier extraction
|
|
6467
|
+
* @param relatedRecords - All related records with full metadata
|
|
5617
6468
|
*/
|
|
5618
6469
|
async enrichTaskRecord(task, relatedRecords) {
|
|
5619
|
-
const { lastUpdated, lastActivityType, recentActivity } = await this.calculateLastUpdated(task, relatedRecords);
|
|
6470
|
+
const { lastUpdated, lastActivityType, recentActivity } = await this.calculateLastUpdated(task.payload, relatedRecords);
|
|
5620
6471
|
return {
|
|
5621
|
-
...task,
|
|
6472
|
+
...task.payload,
|
|
5622
6473
|
lastUpdated,
|
|
5623
6474
|
lastActivityType,
|
|
5624
6475
|
recentActivity
|
|
@@ -6459,7 +7310,6 @@ var workflow_methodology_scrum_default = {
|
|
|
6459
7310
|
required_agents: [
|
|
6460
7311
|
{
|
|
6461
7312
|
id: "agent:scrum-master",
|
|
6462
|
-
gremio: "operations",
|
|
6463
7313
|
engine: {
|
|
6464
7314
|
type: "local",
|
|
6465
7315
|
entrypoint: "@gitgov/agent-scrum-master"
|
|
@@ -6485,7 +7335,6 @@ var workflow_methodology_scrum_default = {
|
|
|
6485
7335
|
},
|
|
6486
7336
|
{
|
|
6487
7337
|
id: "agent:product-owner-assistant",
|
|
6488
|
-
gremio: "strategy",
|
|
6489
7338
|
engine: {
|
|
6490
7339
|
type: "mcp",
|
|
6491
7340
|
url: "http://localhost:8080/product-owner-mcp"
|
|
@@ -6561,11 +7410,19 @@ var WorkflowMethodologyAdapter = class _WorkflowMethodologyAdapter {
|
|
|
6561
7410
|
return this.config;
|
|
6562
7411
|
}
|
|
6563
7412
|
/**
|
|
6564
|
-
*
|
|
7413
|
+
* Determines which signature group to use for validation.
|
|
7414
|
+
* Checks all available signature groups and returns the first one where
|
|
7415
|
+
* the actor has matching capability roles.
|
|
6565
7416
|
*/
|
|
6566
|
-
|
|
6567
|
-
const
|
|
6568
|
-
|
|
7417
|
+
getApplicableSignatureGroup(signatureRules, actor) {
|
|
7418
|
+
for (const [groupName, ruleSet] of Object.entries(signatureRules)) {
|
|
7419
|
+
if (groupName === "__default__") continue;
|
|
7420
|
+
const hasMatchingRole = actor.roles?.some((role) => ruleSet.capability_roles?.includes(role));
|
|
7421
|
+
if (hasMatchingRole) {
|
|
7422
|
+
return groupName;
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
return "__default__";
|
|
6569
7426
|
}
|
|
6570
7427
|
/**
|
|
6571
7428
|
* Determines if a state transition is legal according to the methodology
|
|
@@ -6589,7 +7446,6 @@ var WorkflowMethodologyAdapter = class _WorkflowMethodologyAdapter {
|
|
|
6589
7446
|
*/
|
|
6590
7447
|
async validateSignature(signature, context) {
|
|
6591
7448
|
const config = this.getConfig();
|
|
6592
|
-
const guild = this.getTaskGuild(context);
|
|
6593
7449
|
if (!context.transitionTo) {
|
|
6594
7450
|
throw new Error('ValidationContext must include "transitionTo" for signature validation.');
|
|
6595
7451
|
}
|
|
@@ -6605,7 +7461,8 @@ var WorkflowMethodologyAdapter = class _WorkflowMethodologyAdapter {
|
|
|
6605
7461
|
}
|
|
6606
7462
|
const signatureRules = transitionConfig.requires.signatures;
|
|
6607
7463
|
if (!signatureRules) return true;
|
|
6608
|
-
const
|
|
7464
|
+
const signatureGroup = this.getApplicableSignatureGroup(signatureRules, actor);
|
|
7465
|
+
const ruleSet = signatureRules[signatureGroup];
|
|
6609
7466
|
if (!ruleSet) return false;
|
|
6610
7467
|
if (signature.role !== ruleSet.role) {
|
|
6611
7468
|
return false;
|
|
@@ -6720,9 +7577,11 @@ __export(factories_exports, {
|
|
|
6720
7577
|
createChangelogRecord: () => createChangelogRecord,
|
|
6721
7578
|
createCycleRecord: () => createCycleRecord,
|
|
6722
7579
|
createDefaultWorkflowMethodologyConfig: () => createDefaultWorkflowMethodologyConfig,
|
|
7580
|
+
createEmbeddedMetadataRecord: () => createEmbeddedMetadataRecord,
|
|
6723
7581
|
createExecutionRecord: () => createExecutionRecord,
|
|
6724
7582
|
createFeedbackRecord: () => createFeedbackRecord,
|
|
6725
7583
|
createTaskRecord: () => createTaskRecord,
|
|
7584
|
+
createTestSignature: () => createTestSignature,
|
|
6726
7585
|
createWorkflowMethodologyConfig: () => createWorkflowMethodologyConfig
|
|
6727
7586
|
});
|
|
6728
7587
|
|
|
@@ -6962,6 +7821,68 @@ async function createDefaultWorkflowMethodologyConfig() {
|
|
|
6962
7821
|
});
|
|
6963
7822
|
}
|
|
6964
7823
|
|
|
7824
|
+
// src/factories/embedded_metadata_factory.ts
|
|
7825
|
+
function createTestSignature(keyId = "human:test-user", role = "author", notes = "Test signature - unsigned") {
|
|
7826
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
7827
|
+
return {
|
|
7828
|
+
keyId,
|
|
7829
|
+
role,
|
|
7830
|
+
notes,
|
|
7831
|
+
signature: "dGVzdHNpZ25hdHVyZWJhc2U2NGVuY29kZWRkdW1teWZvcnRlc3RpbmdwdXJwb3Nlc29ubHlub3RyZWFsY3J5cHRvZ3JhcGh5PT0=",
|
|
7832
|
+
// Dummy 88-char base64 for testing (matches Ed25519 signature length)
|
|
7833
|
+
timestamp
|
|
7834
|
+
};
|
|
7835
|
+
}
|
|
7836
|
+
function inferTypeFromPayload(payload) {
|
|
7837
|
+
if ("engine" in payload) return "agent";
|
|
7838
|
+
if ("taskId" in payload && "result" in payload) return "execution";
|
|
7839
|
+
if ("relatedTasks" in payload && "completedAt" in payload) return "changelog";
|
|
7840
|
+
if ("entityType" in payload && "entityId" in payload) return "feedback";
|
|
7841
|
+
if ("status" in payload && "taskIds" in payload) return "cycle";
|
|
7842
|
+
if ("priority" in payload && "description" in payload) return "task";
|
|
7843
|
+
if ("displayName" in payload && "publicKey" in payload) return "actor";
|
|
7844
|
+
return "custom";
|
|
7845
|
+
}
|
|
7846
|
+
async function createEmbeddedMetadataRecord(payload, options = {}) {
|
|
7847
|
+
const inferredType = inferTypeFromPayload(payload);
|
|
7848
|
+
const type = options.header?.type || inferredType;
|
|
7849
|
+
const payloadChecksum = calculatePayloadChecksum(payload);
|
|
7850
|
+
let signatures;
|
|
7851
|
+
if (options.signatures) {
|
|
7852
|
+
signatures = options.signatures;
|
|
7853
|
+
} else if (options.signature?.privateKey) {
|
|
7854
|
+
const keyId = options.signature.keyId || "human:test-user";
|
|
7855
|
+
const role = options.signature.role || "author";
|
|
7856
|
+
const notes = options.signature.notes || "Created via factory";
|
|
7857
|
+
signatures = [signPayload(payload, options.signature.privateKey, keyId, role, notes)];
|
|
7858
|
+
} else {
|
|
7859
|
+
const keyId = options.signature?.keyId || "human:test-user";
|
|
7860
|
+
const role = options.signature?.role || "author";
|
|
7861
|
+
const notes = options.signature?.notes || "Test signature - unsigned";
|
|
7862
|
+
signatures = [createTestSignature(keyId, role, notes)];
|
|
7863
|
+
}
|
|
7864
|
+
const header = {
|
|
7865
|
+
version: "1.0",
|
|
7866
|
+
// Always 1.0 (schema enforces this)
|
|
7867
|
+
type,
|
|
7868
|
+
payloadChecksum,
|
|
7869
|
+
signatures,
|
|
7870
|
+
...type === "custom" && {
|
|
7871
|
+
schemaUrl: options.header?.schemaUrl,
|
|
7872
|
+
schemaChecksum: options.header?.schemaChecksum
|
|
7873
|
+
}
|
|
7874
|
+
};
|
|
7875
|
+
const embeddedRecord = {
|
|
7876
|
+
header,
|
|
7877
|
+
payload
|
|
7878
|
+
};
|
|
7879
|
+
const validation = validateEmbeddedMetadataDetailed(embeddedRecord);
|
|
7880
|
+
if (!validation.isValid) {
|
|
7881
|
+
throw new DetailedValidationError("EmbeddedMetadataRecord", validation.errors);
|
|
7882
|
+
}
|
|
7883
|
+
return embeddedRecord;
|
|
7884
|
+
}
|
|
7885
|
+
|
|
6965
7886
|
// src/validation/index.ts
|
|
6966
7887
|
var validation_exports = {};
|
|
6967
7888
|
__export(validation_exports, {
|
|
@@ -7025,9 +7946,11 @@ function generateSubscriptionId() {
|
|
|
7025
7946
|
var EventBus = class {
|
|
7026
7947
|
emitter;
|
|
7027
7948
|
subscriptions;
|
|
7949
|
+
pendingHandlers;
|
|
7028
7950
|
constructor() {
|
|
7029
7951
|
this.emitter = new EventEmitter();
|
|
7030
7952
|
this.subscriptions = /* @__PURE__ */ new Map();
|
|
7953
|
+
this.pendingHandlers = /* @__PURE__ */ new Set();
|
|
7031
7954
|
this.emitter.setMaxListeners(100);
|
|
7032
7955
|
}
|
|
7033
7956
|
/**
|
|
@@ -7058,11 +7981,17 @@ var EventBus = class {
|
|
|
7058
7981
|
subscribe(eventType, handler) {
|
|
7059
7982
|
const subscriptionId = generateSubscriptionId();
|
|
7060
7983
|
const wrappedHandler = async (event) => {
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7984
|
+
const handlerPromise = (async () => {
|
|
7985
|
+
try {
|
|
7986
|
+
await handler(event);
|
|
7987
|
+
} catch (error) {
|
|
7988
|
+
console.error(`Error in event handler for ${eventType}:`, error);
|
|
7989
|
+
}
|
|
7990
|
+
})();
|
|
7991
|
+
this.pendingHandlers.add(handlerPromise);
|
|
7992
|
+
handlerPromise.finally(() => {
|
|
7993
|
+
this.pendingHandlers.delete(handlerPromise);
|
|
7994
|
+
});
|
|
7066
7995
|
};
|
|
7067
7996
|
const subscription = {
|
|
7068
7997
|
id: subscriptionId,
|
|
@@ -7134,6 +8063,43 @@ var EventBus = class {
|
|
|
7134
8063
|
subscribeToAll(handler) {
|
|
7135
8064
|
return this.subscribe("*", handler);
|
|
7136
8065
|
}
|
|
8066
|
+
/**
|
|
8067
|
+
* Wait for all pending event handlers to complete.
|
|
8068
|
+
* This is primarily useful for testing to ensure event handlers finish before assertions.
|
|
8069
|
+
*
|
|
8070
|
+
* In production, events are fire-and-forget for performance.
|
|
8071
|
+
* In tests, use this to synchronize and avoid race conditions.
|
|
8072
|
+
*
|
|
8073
|
+
* @param options - Optional configuration
|
|
8074
|
+
* @param options.timeout - Maximum time to wait in ms (default: 5000)
|
|
8075
|
+
* @returns Promise that resolves when all handlers complete or timeout occurs
|
|
8076
|
+
*
|
|
8077
|
+
* @example
|
|
8078
|
+
* ```typescript
|
|
8079
|
+
* await feedbackAdapter.create(...); // publishes event
|
|
8080
|
+
* await eventBus.waitForIdle(); // wait for BacklogAdapter.handleFeedbackCreated()
|
|
8081
|
+
* const task = await backlogAdapter.getTask(taskId);
|
|
8082
|
+
* expect(task.status).toBe('paused'); // now safe to assert
|
|
8083
|
+
* ```
|
|
8084
|
+
*/
|
|
8085
|
+
async waitForIdle(options = {}) {
|
|
8086
|
+
const timeout = options.timeout ?? 5e3;
|
|
8087
|
+
const startTime = Date.now();
|
|
8088
|
+
while (this.pendingHandlers.size > 0) {
|
|
8089
|
+
if (Date.now() - startTime > timeout) {
|
|
8090
|
+
const pendingCount = this.pendingHandlers.size;
|
|
8091
|
+
console.warn(`EventBus.waitForIdle() timeout after ${timeout}ms with ${pendingCount} handlers still pending`);
|
|
8092
|
+
break;
|
|
8093
|
+
}
|
|
8094
|
+
if (this.pendingHandlers.size > 0) {
|
|
8095
|
+
await Promise.race([
|
|
8096
|
+
Promise.all(Array.from(this.pendingHandlers)),
|
|
8097
|
+
new Promise((resolve) => setTimeout(resolve, 10))
|
|
8098
|
+
// Re-check every 10ms
|
|
8099
|
+
]);
|
|
8100
|
+
}
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
7137
8103
|
};
|
|
7138
8104
|
var eventBus = new EventBus();
|
|
7139
8105
|
function publishEvent(event) {
|
|
@@ -7327,14 +8293,17 @@ var RelationshipAnalyzer = class {
|
|
|
7327
8293
|
for (const task of tasks) {
|
|
7328
8294
|
const isEpic = this.isEpicTask(task);
|
|
7329
8295
|
const title = task.title || "Untitled Task";
|
|
7330
|
-
|
|
8296
|
+
const node = {
|
|
7331
8297
|
id: this.generateNodeId(task),
|
|
7332
8298
|
type: isEpic ? "epic-task" : "task",
|
|
7333
8299
|
title,
|
|
7334
8300
|
status: task.status,
|
|
7335
|
-
tags: task.tags,
|
|
7336
8301
|
originalId: task.id
|
|
7337
|
-
}
|
|
8302
|
+
};
|
|
8303
|
+
if (task.tags) {
|
|
8304
|
+
node.tags = task.tags;
|
|
8305
|
+
}
|
|
8306
|
+
nodes.push(node);
|
|
7338
8307
|
}
|
|
7339
8308
|
return nodes;
|
|
7340
8309
|
}
|