@gitgov/core 1.3.0 → 1.5.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 +2110 -529
- package/dist/src/index.js +1750 -799
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { promises, existsSync, constants } from 'fs';
|
|
|
5
5
|
import * as yaml from 'js-yaml';
|
|
6
6
|
import { generateKeyPair, createHash, sign, verify } from 'crypto';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
-
import * as
|
|
8
|
+
import * as path from 'path';
|
|
9
9
|
import { EventEmitter } from 'events';
|
|
10
10
|
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
@@ -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"
|
|
619
|
+
},
|
|
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)"
|
|
338
628
|
},
|
|
339
|
-
|
|
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"
|
|
877
|
+
],
|
|
878
|
+
tags: [
|
|
879
|
+
"sprint:24",
|
|
880
|
+
"team:backend",
|
|
881
|
+
"focus:performance"
|
|
882
|
+
],
|
|
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"
|
|
576
910
|
],
|
|
577
911
|
tags: [
|
|
578
|
-
"roadmap:q4"
|
|
912
|
+
"roadmap:q4",
|
|
913
|
+
"strategy:growth",
|
|
914
|
+
"okr:scale-to-1m-users"
|
|
579
915
|
],
|
|
580
|
-
notes: "Objetivo:
|
|
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: [
|
|
@@ -882,9 +1220,9 @@ var embedded_metadata_schema_default = {
|
|
|
882
1220
|
{
|
|
883
1221
|
keyId: "human:lead-dev",
|
|
884
1222
|
role: "author",
|
|
1223
|
+
notes: "Initial task creation for OAuth 2.0 implementation",
|
|
885
1224
|
signature: "...",
|
|
886
|
-
timestamp: 1752274500
|
|
887
|
-
timestamp_iso: "2025-07-25T14:30:00Z"
|
|
1225
|
+
timestamp: 1752274500
|
|
888
1226
|
}
|
|
889
1227
|
]
|
|
890
1228
|
},
|
|
@@ -898,6 +1236,68 @@ var embedded_metadata_schema_default = {
|
|
|
898
1236
|
"area:backend"
|
|
899
1237
|
]
|
|
900
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
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
header: {
|
|
1272
|
+
version: "1.0",
|
|
1273
|
+
type: "actor",
|
|
1274
|
+
payloadChecksum: "c3d4e5f6a1b2...",
|
|
1275
|
+
signatures: [
|
|
1276
|
+
{
|
|
1277
|
+
keyId: "human:admin",
|
|
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.",
|
|
1287
|
+
signature: "...",
|
|
1288
|
+
timestamp: 1752274705
|
|
1289
|
+
}
|
|
1290
|
+
]
|
|
1291
|
+
},
|
|
1292
|
+
payload: {
|
|
1293
|
+
id: "human:new-developer",
|
|
1294
|
+
type: "human",
|
|
1295
|
+
displayName: "New Developer",
|
|
1296
|
+
publicKey: "...",
|
|
1297
|
+
roles: [
|
|
1298
|
+
"developer"
|
|
1299
|
+
]
|
|
1300
|
+
}
|
|
901
1301
|
}
|
|
902
1302
|
]
|
|
903
1303
|
};
|
|
@@ -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.",
|
|
989
1418
|
references: [
|
|
990
|
-
"
|
|
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.",
|
|
1454
|
+
references: [
|
|
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
|
|
|
@@ -1819,18 +2672,20 @@ var logger2 = createLogger("[CryptoModule] ");
|
|
|
1819
2672
|
var generateKeyPairAsync = promisify(generateKeyPair);
|
|
1820
2673
|
async function generateKeys() {
|
|
1821
2674
|
const { publicKey, privateKey } = await generateKeyPairAsync("ed25519", {
|
|
1822
|
-
publicKeyEncoding: { type: "spki", format: "
|
|
2675
|
+
publicKeyEncoding: { type: "spki", format: "der" },
|
|
1823
2676
|
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
1824
2677
|
});
|
|
2678
|
+
const rawPublicKey = publicKey.subarray(-32);
|
|
1825
2679
|
return {
|
|
1826
|
-
publicKey:
|
|
2680
|
+
publicKey: rawPublicKey.toString("base64"),
|
|
2681
|
+
// 32 bytes -> 44 chars
|
|
1827
2682
|
privateKey: Buffer.from(privateKey).toString("base64")
|
|
1828
2683
|
};
|
|
1829
2684
|
}
|
|
1830
|
-
function signPayload(payload, privateKey, keyId, role) {
|
|
2685
|
+
function signPayload(payload, privateKey, keyId, role, notes) {
|
|
1831
2686
|
const payloadChecksum = calculatePayloadChecksum(payload);
|
|
1832
2687
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
1833
|
-
const digest = `${payloadChecksum}:${keyId}:${role}:${timestamp}`;
|
|
2688
|
+
const digest = `${payloadChecksum}:${keyId}:${role}:${notes}:${timestamp}`;
|
|
1834
2689
|
const digestHash = createHash("sha256").update(digest).digest();
|
|
1835
2690
|
const signature = sign(null, digestHash, {
|
|
1836
2691
|
key: Buffer.from(privateKey, "base64"),
|
|
@@ -1840,27 +2695,43 @@ function signPayload(payload, privateKey, keyId, role) {
|
|
|
1840
2695
|
return {
|
|
1841
2696
|
keyId,
|
|
1842
2697
|
role,
|
|
2698
|
+
notes,
|
|
1843
2699
|
signature: signature.toString("base64"),
|
|
1844
|
-
timestamp
|
|
1845
|
-
timestamp_iso: new Date(timestamp * 1e3).toISOString()
|
|
2700
|
+
timestamp
|
|
1846
2701
|
};
|
|
1847
2702
|
}
|
|
1848
2703
|
async function verifySignatures(record, getActorPublicKey) {
|
|
1849
2704
|
for (const signature of record.header.signatures) {
|
|
1850
|
-
const
|
|
1851
|
-
if (!
|
|
2705
|
+
const publicKeyBase64 = await getActorPublicKey(signature.keyId);
|
|
2706
|
+
if (!publicKeyBase64) {
|
|
1852
2707
|
logger2.warn(`Public key not found for actor: ${signature.keyId}`);
|
|
1853
2708
|
return false;
|
|
1854
2709
|
}
|
|
1855
|
-
const digest = `${record.header.payloadChecksum}:${signature.keyId}:${signature.role}:${signature.timestamp}`;
|
|
2710
|
+
const digest = `${record.header.payloadChecksum}:${signature.keyId}:${signature.role}:${signature.notes}:${signature.timestamp}`;
|
|
1856
2711
|
const digestHash = createHash("sha256").update(digest).digest();
|
|
2712
|
+
const algorithmIdentifier = Buffer.from([
|
|
2713
|
+
48,
|
|
2714
|
+
42,
|
|
2715
|
+
48,
|
|
2716
|
+
5,
|
|
2717
|
+
6,
|
|
2718
|
+
3,
|
|
2719
|
+
43,
|
|
2720
|
+
101,
|
|
2721
|
+
112,
|
|
2722
|
+
3,
|
|
2723
|
+
33,
|
|
2724
|
+
0
|
|
2725
|
+
]);
|
|
2726
|
+
const rawPublicKey = Buffer.from(publicKeyBase64, "base64");
|
|
2727
|
+
const spkiPublicKey = Buffer.concat([algorithmIdentifier, rawPublicKey]);
|
|
1857
2728
|
const isValid = verify(
|
|
1858
2729
|
null,
|
|
1859
2730
|
digestHash,
|
|
1860
2731
|
{
|
|
1861
|
-
key:
|
|
2732
|
+
key: spkiPublicKey,
|
|
1862
2733
|
type: "spki",
|
|
1863
|
-
format: "
|
|
2734
|
+
format: "der"
|
|
1864
2735
|
},
|
|
1865
2736
|
Buffer.from(signature.signature, "base64")
|
|
1866
2737
|
);
|
|
@@ -1955,13 +2826,6 @@ function validateEmbeddedMetadataBusinessRules(data) {
|
|
|
1955
2826
|
value: data.header.signatures
|
|
1956
2827
|
});
|
|
1957
2828
|
}
|
|
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
2829
|
return {
|
|
1966
2830
|
isValid: errors.length === 0,
|
|
1967
2831
|
errors
|
|
@@ -1981,8 +2845,8 @@ function isTaskRecord(data) {
|
|
|
1981
2845
|
function validateTaskRecordDetailed(data) {
|
|
1982
2846
|
const [isValid, ajvErrors] = validateTaskRecordSchema(data);
|
|
1983
2847
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
1984
|
-
field: error.instancePath || error.
|
|
1985
|
-
message: error.message || "
|
|
2848
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2849
|
+
message: error.message || "Unknown validation error",
|
|
1986
2850
|
value: error.data
|
|
1987
2851
|
})) : [];
|
|
1988
2852
|
return {
|
|
@@ -1993,9 +2857,12 @@ function validateTaskRecordDetailed(data) {
|
|
|
1993
2857
|
async function validateFullTaskRecord(record, getActorPublicKey) {
|
|
1994
2858
|
const [isValidSchema, errors] = validateTaskRecordSchema(record.payload);
|
|
1995
2859
|
if (!isValidSchema) {
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2860
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
2861
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2862
|
+
message: error.message || "Unknown validation error",
|
|
2863
|
+
value: error.data
|
|
2864
|
+
}));
|
|
2865
|
+
throw new DetailedValidationError("TaskRecord", formattedErrors);
|
|
1999
2866
|
}
|
|
2000
2867
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2001
2868
|
}
|
|
@@ -2020,32 +2887,14 @@ function generateExecutionId(title, timestamp) {
|
|
|
2020
2887
|
const slug = sanitizeForId(title);
|
|
2021
2888
|
return `${timestamp}-exec-${slug}`;
|
|
2022
2889
|
}
|
|
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}`;
|
|
2890
|
+
function generateChangelogId(title, timestamp) {
|
|
2891
|
+
const slug = sanitizeForId(title);
|
|
2892
|
+
return `${timestamp}-changelog-${slug}`;
|
|
2032
2893
|
}
|
|
2033
2894
|
function generateFeedbackId(title, timestamp) {
|
|
2034
2895
|
const slug = sanitizeForId(title);
|
|
2035
2896
|
return `${timestamp}-feedback-${slug}`;
|
|
2036
2897
|
}
|
|
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
2898
|
|
|
2050
2899
|
// src/factories/task_factory.ts
|
|
2051
2900
|
async function createTaskRecord(payload) {
|
|
@@ -2083,8 +2932,8 @@ function isCycleRecord(data) {
|
|
|
2083
2932
|
function validateCycleRecordDetailed(data) {
|
|
2084
2933
|
const [isValid, ajvErrors] = validateCycleRecordSchema(data);
|
|
2085
2934
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
2086
|
-
field: error.instancePath || error.
|
|
2087
|
-
message: error.message || "
|
|
2935
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2936
|
+
message: error.message || "Unknown validation error",
|
|
2088
2937
|
value: error.data
|
|
2089
2938
|
})) : [];
|
|
2090
2939
|
return {
|
|
@@ -2095,9 +2944,12 @@ function validateCycleRecordDetailed(data) {
|
|
|
2095
2944
|
async function validateFullCycleRecord(record, getActorPublicKey) {
|
|
2096
2945
|
const [isValidSchema, errors] = validateCycleRecordSchema(record.payload);
|
|
2097
2946
|
if (!isValidSchema) {
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2947
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
2948
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
2949
|
+
message: error.message || "Unknown validation error",
|
|
2950
|
+
value: error.data
|
|
2951
|
+
}));
|
|
2952
|
+
throw new DetailedValidationError("CycleRecord", formattedErrors);
|
|
2101
2953
|
}
|
|
2102
2954
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2103
2955
|
}
|
|
@@ -2141,8 +2993,8 @@ var ConfigManager = class _ConfigManager {
|
|
|
2141
2993
|
configPath;
|
|
2142
2994
|
sessionPath;
|
|
2143
2995
|
constructor(projectRootPath = _ConfigManager.findProjectRoot() || process.cwd()) {
|
|
2144
|
-
this.configPath =
|
|
2145
|
-
this.sessionPath =
|
|
2996
|
+
this.configPath = path.join(projectRootPath, ".gitgov", "config.json");
|
|
2997
|
+
this.sessionPath = path.join(projectRootPath, ".gitgov", ".session.json");
|
|
2146
2998
|
}
|
|
2147
2999
|
/**
|
|
2148
3000
|
* Load GitGovernance configuration
|
|
@@ -2232,14 +3084,14 @@ var ConfigManager = class _ConfigManager {
|
|
|
2232
3084
|
}
|
|
2233
3085
|
lastSearchPath = startPath;
|
|
2234
3086
|
let currentPath = startPath;
|
|
2235
|
-
while (currentPath !==
|
|
2236
|
-
if (existsSync(
|
|
3087
|
+
while (currentPath !== path.parse(currentPath).root) {
|
|
3088
|
+
if (existsSync(path.join(currentPath, ".git"))) {
|
|
2237
3089
|
projectRoot = currentPath;
|
|
2238
3090
|
return projectRoot;
|
|
2239
3091
|
}
|
|
2240
|
-
currentPath =
|
|
3092
|
+
currentPath = path.dirname(currentPath);
|
|
2241
3093
|
}
|
|
2242
|
-
if (existsSync(
|
|
3094
|
+
if (existsSync(path.join(currentPath, ".git"))) {
|
|
2243
3095
|
projectRoot = currentPath;
|
|
2244
3096
|
return projectRoot;
|
|
2245
3097
|
}
|
|
@@ -2253,23 +3105,23 @@ var ConfigManager = class _ConfigManager {
|
|
|
2253
3105
|
*/
|
|
2254
3106
|
static findGitgovRoot(startPath = process.cwd()) {
|
|
2255
3107
|
let currentPath = startPath;
|
|
2256
|
-
while (currentPath !==
|
|
2257
|
-
if (existsSync(
|
|
3108
|
+
while (currentPath !== path.parse(currentPath).root) {
|
|
3109
|
+
if (existsSync(path.join(currentPath, ".gitgov"))) {
|
|
2258
3110
|
return currentPath;
|
|
2259
3111
|
}
|
|
2260
|
-
currentPath =
|
|
3112
|
+
currentPath = path.dirname(currentPath);
|
|
2261
3113
|
}
|
|
2262
|
-
if (existsSync(
|
|
3114
|
+
if (existsSync(path.join(currentPath, ".gitgov"))) {
|
|
2263
3115
|
return currentPath;
|
|
2264
3116
|
}
|
|
2265
3117
|
currentPath = startPath;
|
|
2266
|
-
while (currentPath !==
|
|
2267
|
-
if (existsSync(
|
|
3118
|
+
while (currentPath !== path.parse(currentPath).root) {
|
|
3119
|
+
if (existsSync(path.join(currentPath, ".git"))) {
|
|
2268
3120
|
return currentPath;
|
|
2269
3121
|
}
|
|
2270
|
-
currentPath =
|
|
3122
|
+
currentPath = path.dirname(currentPath);
|
|
2271
3123
|
}
|
|
2272
|
-
if (existsSync(
|
|
3124
|
+
if (existsSync(path.join(currentPath, ".git"))) {
|
|
2273
3125
|
return currentPath;
|
|
2274
3126
|
}
|
|
2275
3127
|
return null;
|
|
@@ -2282,7 +3134,7 @@ var ConfigManager = class _ConfigManager {
|
|
|
2282
3134
|
if (!root) {
|
|
2283
3135
|
throw new Error("Could not find project root. Make sure you are inside a GitGovernance repository.");
|
|
2284
3136
|
}
|
|
2285
|
-
return
|
|
3137
|
+
return path.join(root, ".gitgov");
|
|
2286
3138
|
}
|
|
2287
3139
|
/**
|
|
2288
3140
|
* Checks if current directory is a GitGovernance project
|
|
@@ -2311,12 +3163,12 @@ var RecordStore = class {
|
|
|
2311
3163
|
throw new Error("Could not find project root. RecordStore requires a valid project root.");
|
|
2312
3164
|
}
|
|
2313
3165
|
this.recordType = recordType;
|
|
2314
|
-
this.recordsDir =
|
|
3166
|
+
this.recordsDir = path.join(foundRoot, ".gitgov", this.recordType);
|
|
2315
3167
|
this.fs = fsDeps;
|
|
2316
3168
|
}
|
|
2317
3169
|
getRecordPath(recordId) {
|
|
2318
3170
|
const safeId = recordId.replace(/:/g, "_");
|
|
2319
|
-
return
|
|
3171
|
+
return path.join(this.recordsDir, `${safeId}.json`);
|
|
2320
3172
|
}
|
|
2321
3173
|
async ensureDirExists() {
|
|
2322
3174
|
await this.fs.mkdir(this.recordsDir, { recursive: true });
|
|
@@ -2395,8 +3247,8 @@ function isActorRecord(data) {
|
|
|
2395
3247
|
function validateActorRecordDetailed(data) {
|
|
2396
3248
|
const [isValid, ajvErrors] = validateActorRecordSchema(data);
|
|
2397
3249
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
2398
|
-
field: error.instancePath || error.
|
|
2399
|
-
message: error.message || "
|
|
3250
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3251
|
+
message: error.message || "Unknown validation error",
|
|
2400
3252
|
value: error.data
|
|
2401
3253
|
})) : [];
|
|
2402
3254
|
return {
|
|
@@ -2407,9 +3259,12 @@ function validateActorRecordDetailed(data) {
|
|
|
2407
3259
|
async function validateFullActorRecord(record, getActorPublicKey) {
|
|
2408
3260
|
const [isValidSchema, errors] = validateActorRecordSchema(record.payload);
|
|
2409
3261
|
if (!isValidSchema) {
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
3262
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
3263
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3264
|
+
message: error.message || "Unknown validation error",
|
|
3265
|
+
value: error.data
|
|
3266
|
+
}));
|
|
3267
|
+
throw new DetailedValidationError("ActorRecord", formattedErrors);
|
|
2413
3268
|
}
|
|
2414
3269
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2415
3270
|
}
|
|
@@ -2445,8 +3300,8 @@ function isAgentRecord(data) {
|
|
|
2445
3300
|
function validateAgentRecordDetailed(data) {
|
|
2446
3301
|
const [isValid, ajvErrors] = validateAgentRecordSchema(data);
|
|
2447
3302
|
const formattedErrors = ajvErrors ? ajvErrors.map((error) => ({
|
|
2448
|
-
field: error.instancePath || error.
|
|
2449
|
-
message: error.message || "
|
|
3303
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3304
|
+
message: error.message || "Unknown validation error",
|
|
2450
3305
|
value: error.data
|
|
2451
3306
|
})) : [];
|
|
2452
3307
|
return {
|
|
@@ -2457,9 +3312,12 @@ function validateAgentRecordDetailed(data) {
|
|
|
2457
3312
|
async function validateFullAgentRecord(record, getActorPublicKey) {
|
|
2458
3313
|
const [isValidSchema, errors] = validateAgentRecordSchema(record.payload);
|
|
2459
3314
|
if (!isValidSchema) {
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
3315
|
+
const formattedErrors = (errors || []).map((error) => ({
|
|
3316
|
+
field: error.instancePath?.replace("/", "") || error.params?.["missingProperty"] || "root",
|
|
3317
|
+
message: error.message || "Unknown validation error",
|
|
3318
|
+
value: error.data
|
|
3319
|
+
}));
|
|
3320
|
+
throw new DetailedValidationError("AgentRecord", formattedErrors);
|
|
2463
3321
|
}
|
|
2464
3322
|
await validateFullEmbeddedMetadataRecord(record, getActorPublicKey);
|
|
2465
3323
|
}
|
|
@@ -2486,7 +3344,6 @@ async function validateAgentActorRelationship(agentRecord, getEffectiveActor) {
|
|
|
2486
3344
|
async function createAgentRecord(payload) {
|
|
2487
3345
|
const agent = {
|
|
2488
3346
|
id: payload.id || "",
|
|
2489
|
-
guild: payload.guild || "design",
|
|
2490
3347
|
engine: payload.engine || { type: "local" },
|
|
2491
3348
|
status: payload.status || "active",
|
|
2492
3349
|
triggers: payload.triggers || [],
|
|
@@ -2539,7 +3396,7 @@ var IdentityAdapter = class {
|
|
|
2539
3396
|
};
|
|
2540
3397
|
const validatedPayload = await createActorRecord(completePayload);
|
|
2541
3398
|
const payloadChecksum = calculatePayloadChecksum(validatedPayload);
|
|
2542
|
-
const signature = await signPayload(validatedPayload, privateKey, actorId, "author");
|
|
3399
|
+
const signature = await signPayload(validatedPayload, privateKey, actorId, "author", "Actor registration");
|
|
2543
3400
|
const record = {
|
|
2544
3401
|
header: {
|
|
2545
3402
|
version: "1.0",
|
|
@@ -2601,9 +3458,9 @@ var IdentityAdapter = class {
|
|
|
2601
3458
|
const mockSignature = {
|
|
2602
3459
|
keyId: actorId,
|
|
2603
3460
|
role,
|
|
3461
|
+
notes: "Record signed",
|
|
2604
3462
|
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()
|
|
3463
|
+
timestamp: Math.floor(Date.now() / 1e3)
|
|
2607
3464
|
};
|
|
2608
3465
|
const signedRecord = {
|
|
2609
3466
|
...record,
|
|
@@ -2711,8 +3568,8 @@ var IdentityAdapter = class {
|
|
|
2711
3568
|
console.warn("authenticate not fully implemented yet");
|
|
2712
3569
|
}
|
|
2713
3570
|
async createAgentRecord(payload) {
|
|
2714
|
-
if (!payload.id || !payload.
|
|
2715
|
-
throw new Error("AgentRecord requires id
|
|
3571
|
+
if (!payload.id || !payload.engine) {
|
|
3572
|
+
throw new Error("AgentRecord requires id and engine");
|
|
2716
3573
|
}
|
|
2717
3574
|
const correspondingActor = await this.getActor(payload.id);
|
|
2718
3575
|
if (!correspondingActor) {
|
|
@@ -2723,7 +3580,6 @@ var IdentityAdapter = class {
|
|
|
2723
3580
|
}
|
|
2724
3581
|
const completePayload = {
|
|
2725
3582
|
id: payload.id,
|
|
2726
|
-
guild: payload.guild,
|
|
2727
3583
|
engine: payload.engine,
|
|
2728
3584
|
status: payload.status || "active",
|
|
2729
3585
|
triggers: payload.triggers || [],
|
|
@@ -2733,7 +3589,7 @@ var IdentityAdapter = class {
|
|
|
2733
3589
|
};
|
|
2734
3590
|
const validatedPayload = await createAgentRecord(completePayload);
|
|
2735
3591
|
const payloadChecksum = calculatePayloadChecksum(validatedPayload);
|
|
2736
|
-
const signature = signPayload(validatedPayload, "placeholder-private-key", payload.id, "author");
|
|
3592
|
+
const signature = signPayload(validatedPayload, "placeholder-private-key", payload.id, "author", "Agent registration");
|
|
2737
3593
|
const record = {
|
|
2738
3594
|
header: {
|
|
2739
3595
|
version: "1.0",
|
|
@@ -2758,7 +3614,6 @@ var IdentityAdapter = class {
|
|
|
2758
3614
|
source: "identity_adapter",
|
|
2759
3615
|
payload: {
|
|
2760
3616
|
agentId: validatedPayload.id,
|
|
2761
|
-
guild: validatedPayload.guild,
|
|
2762
3617
|
engine: validatedPayload.engine,
|
|
2763
3618
|
correspondingActorId: correspondingActor.id
|
|
2764
3619
|
}
|
|
@@ -2863,11 +3718,11 @@ var FeedbackAdapter = class {
|
|
|
2863
3718
|
this.eventBus = dependencies.eventBus;
|
|
2864
3719
|
}
|
|
2865
3720
|
/**
|
|
2866
|
-
* [EARS-1] Creates a new FeedbackRecord for structured communication between actors.
|
|
3721
|
+
* [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
3722
|
*
|
|
2868
3723
|
* Description: Creates a new FeedbackRecord for structured communication between actors.
|
|
2869
3724
|
* Implementation: Builds record with status: "open", signs with actorId, persists and emits event.
|
|
2870
|
-
* Usage: Invoked by `gitgov feedback
|
|
3725
|
+
* Usage: Invoked by `gitgov feedback create` to create feedback, assignments, blocks, responses.
|
|
2871
3726
|
* Returns: Complete and signed FeedbackRecord.
|
|
2872
3727
|
*/
|
|
2873
3728
|
async create(payload, actorId) {
|
|
@@ -2875,21 +3730,30 @@ var FeedbackAdapter = class {
|
|
|
2875
3730
|
if (!payloadWithEntityId.entityId) {
|
|
2876
3731
|
throw new Error("RecordNotFoundError: entityId is required");
|
|
2877
3732
|
}
|
|
2878
|
-
if (payloadWithEntityId.entityType && !["task", "execution", "changelog", "feedback"].includes(payloadWithEntityId.entityType)) {
|
|
2879
|
-
throw new Error("InvalidEntityTypeError: entityType must be task, execution, changelog, or
|
|
3733
|
+
if (payloadWithEntityId.entityType && !["task", "execution", "changelog", "feedback", "cycle"].includes(payloadWithEntityId.entityType)) {
|
|
3734
|
+
throw new Error("InvalidEntityTypeError: entityType must be task, execution, changelog, feedback, or cycle");
|
|
2880
3735
|
}
|
|
2881
3736
|
if (payload.type === "assignment" && payload.assignee) {
|
|
2882
3737
|
const existingFeedbacks = await this.getFeedbackByEntity(payloadWithEntityId.entityId);
|
|
2883
|
-
const
|
|
3738
|
+
const openAssignments = existingFeedbacks.filter(
|
|
2884
3739
|
(feedback) => feedback.type === "assignment" && feedback.assignee === payload.assignee && feedback.status === "open"
|
|
2885
3740
|
);
|
|
2886
|
-
if (
|
|
2887
|
-
|
|
3741
|
+
if (openAssignments.length > 0) {
|
|
3742
|
+
const allFeedbacks = await this.getAllFeedback();
|
|
3743
|
+
for (const assignment of openAssignments) {
|
|
3744
|
+
const hasResolution = allFeedbacks.some(
|
|
3745
|
+
(feedback) => feedback.entityType === "feedback" && feedback.resolvesFeedbackId === assignment.id && feedback.status === "resolved"
|
|
3746
|
+
);
|
|
3747
|
+
if (!hasResolution) {
|
|
3748
|
+
throw new Error(`DuplicateAssignmentError: Task ${payloadWithEntityId.entityId} is already assigned to ${payload.assignee} (feedback: ${assignment.id})`);
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
2888
3751
|
}
|
|
2889
3752
|
}
|
|
2890
3753
|
const enrichedPayload = {
|
|
2891
|
-
|
|
2892
|
-
|
|
3754
|
+
status: "open",
|
|
3755
|
+
...payload
|
|
3756
|
+
// Allows payload.status to override default
|
|
2893
3757
|
};
|
|
2894
3758
|
try {
|
|
2895
3759
|
const validatedPayload = await createFeedbackRecord(enrichedPayload);
|
|
@@ -2901,9 +3765,9 @@ var FeedbackAdapter = class {
|
|
|
2901
3765
|
signatures: [{
|
|
2902
3766
|
keyId: actorId,
|
|
2903
3767
|
role: "author",
|
|
3768
|
+
notes: "Feedback created",
|
|
2904
3769
|
signature: "placeholder",
|
|
2905
|
-
timestamp: Date.now()
|
|
2906
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
3770
|
+
timestamp: Date.now()
|
|
2907
3771
|
}]
|
|
2908
3772
|
},
|
|
2909
3773
|
payload: validatedPayload
|
|
@@ -2922,7 +3786,8 @@ var FeedbackAdapter = class {
|
|
|
2922
3786
|
status: validatedPayload.status,
|
|
2923
3787
|
content: validatedPayload.content,
|
|
2924
3788
|
triggeredBy: actorId,
|
|
2925
|
-
assignee: validatedPayload.assignee
|
|
3789
|
+
assignee: validatedPayload.assignee,
|
|
3790
|
+
resolvesFeedbackId: validatedPayload.resolvesFeedbackId
|
|
2926
3791
|
}
|
|
2927
3792
|
});
|
|
2928
3793
|
return validatedPayload;
|
|
@@ -2934,51 +3799,30 @@ var FeedbackAdapter = class {
|
|
|
2934
3799
|
}
|
|
2935
3800
|
}
|
|
2936
3801
|
/**
|
|
2937
|
-
* [EARS-
|
|
3802
|
+
* [EARS-9, EARS-10, EARS-11, EARS-12] Helper: Creates a new feedback that "resolves" another (immutable).
|
|
2938
3803
|
*
|
|
2939
|
-
* Description:
|
|
2940
|
-
* Implementation:
|
|
2941
|
-
* Usage:
|
|
2942
|
-
* Returns:
|
|
2943
|
-
*/
|
|
2944
|
-
async resolve(feedbackId, actorId) {
|
|
2945
|
-
const
|
|
2946
|
-
if (!
|
|
3804
|
+
* Description: Helper method that creates a new feedback documenting resolution of another feedback.
|
|
3805
|
+
* Implementation: Verifies original exists, then delegates to create() with immutable pattern.
|
|
3806
|
+
* Usage: Ergonomic helper for common case. For advanced cases (wontfix, approval), use create() directly.
|
|
3807
|
+
* Returns: New FeedbackRecord that points to the original with resolvesFeedbackId.
|
|
3808
|
+
*/
|
|
3809
|
+
async resolve(feedbackId, actorId, content) {
|
|
3810
|
+
const originalFeedback = await this.getFeedback(feedbackId);
|
|
3811
|
+
if (!originalFeedback) {
|
|
2947
3812
|
throw new Error(`RecordNotFoundError: Feedback not found: ${feedbackId}`);
|
|
2948
3813
|
}
|
|
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
|
-
}
|
|
3814
|
+
const resolveContent = content || `Feedback resolved by ${actorId}`;
|
|
3815
|
+
return await this.create({
|
|
3816
|
+
entityType: "feedback",
|
|
3817
|
+
entityId: feedbackId,
|
|
3818
|
+
type: "clarification",
|
|
3819
|
+
status: "resolved",
|
|
3820
|
+
content: resolveContent,
|
|
3821
|
+
resolvesFeedbackId: feedbackId
|
|
3822
|
+
}, actorId);
|
|
2979
3823
|
}
|
|
2980
3824
|
/**
|
|
2981
|
-
* [EARS-
|
|
3825
|
+
* [EARS-13, EARS-14] Gets a specific FeedbackRecord by its ID for query.
|
|
2982
3826
|
*
|
|
2983
3827
|
* Description: Gets a specific FeedbackRecord by its ID for query.
|
|
2984
3828
|
* Implementation: Direct read from record store without modifications.
|
|
@@ -2990,11 +3834,11 @@ var FeedbackAdapter = class {
|
|
|
2990
3834
|
return record ? record.payload : null;
|
|
2991
3835
|
}
|
|
2992
3836
|
/**
|
|
2993
|
-
* [EARS-
|
|
3837
|
+
* [EARS-15] Gets all FeedbackRecords associated with a specific entity.
|
|
2994
3838
|
*
|
|
2995
3839
|
* Description: Gets all FeedbackRecords associated with a specific entity.
|
|
2996
3840
|
* Implementation: Reads all records and filters by matching entityId.
|
|
2997
|
-
* Usage: Invoked by `gitgov feedback list` to display feedback for a task/cycle.
|
|
3841
|
+
* Usage: Invoked by `gitgov feedback list` to display feedback for a task/cycle/execution.
|
|
2998
3842
|
* Returns: Array of FeedbackRecords filtered for the entity.
|
|
2999
3843
|
*/
|
|
3000
3844
|
async getFeedbackByEntity(entityId) {
|
|
@@ -3009,11 +3853,11 @@ var FeedbackAdapter = class {
|
|
|
3009
3853
|
return feedbacks;
|
|
3010
3854
|
}
|
|
3011
3855
|
/**
|
|
3012
|
-
* [EARS-
|
|
3856
|
+
* [EARS-16] Gets all FeedbackRecords in the system for indexation.
|
|
3013
3857
|
*
|
|
3014
3858
|
* Description: Gets all FeedbackRecords in the system for complete indexation.
|
|
3015
3859
|
* Implementation: Complete read from record store without filters.
|
|
3016
|
-
* Usage: Invoked by `gitgov feedback list
|
|
3860
|
+
* Usage: Invoked by `gitgov feedback list` and by MetricsAdapter for calculations.
|
|
3017
3861
|
* Returns: Complete array of all FeedbackRecords.
|
|
3018
3862
|
*/
|
|
3019
3863
|
async getAllFeedback() {
|
|
@@ -3027,6 +3871,49 @@ var FeedbackAdapter = class {
|
|
|
3027
3871
|
}
|
|
3028
3872
|
return feedbacks;
|
|
3029
3873
|
}
|
|
3874
|
+
/**
|
|
3875
|
+
* [EARS-17, EARS-18, EARS-19, EARS-20] Builds the complete conversation tree for a feedback.
|
|
3876
|
+
*
|
|
3877
|
+
* Description: Recursively constructs the conversation tree for a feedback.
|
|
3878
|
+
* Implementation: Reads root feedback, finds all responses, builds tree recursively until maxDepth.
|
|
3879
|
+
* Usage: Invoked by `gitgov feedback thread` and `gitgov feedback show --thread`.
|
|
3880
|
+
* Returns: FeedbackThread object with tree structure.
|
|
3881
|
+
*/
|
|
3882
|
+
async getFeedbackThread(feedbackId, maxDepth = Infinity) {
|
|
3883
|
+
return await this.buildThread(feedbackId, maxDepth, 0);
|
|
3884
|
+
}
|
|
3885
|
+
/**
|
|
3886
|
+
* Private helper: Recursively builds conversation thread.
|
|
3887
|
+
*/
|
|
3888
|
+
async buildThread(feedbackId, maxDepth, currentDepth) {
|
|
3889
|
+
if (currentDepth >= maxDepth) {
|
|
3890
|
+
throw new Error(`Max depth ${maxDepth} reached for feedback thread`);
|
|
3891
|
+
}
|
|
3892
|
+
const feedback = await this.getFeedback(feedbackId);
|
|
3893
|
+
if (!feedback) {
|
|
3894
|
+
throw new Error(`RecordNotFoundError: Feedback not found: ${feedbackId}`);
|
|
3895
|
+
}
|
|
3896
|
+
const allFeedbacks = await this.getAllFeedback();
|
|
3897
|
+
const responses = allFeedbacks.filter(
|
|
3898
|
+
(f) => f.entityType === "feedback" && f.entityId === feedbackId
|
|
3899
|
+
);
|
|
3900
|
+
const responseThreads = [];
|
|
3901
|
+
for (const response of responses) {
|
|
3902
|
+
try {
|
|
3903
|
+
const thread = await this.buildThread(response.id, maxDepth, currentDepth + 1);
|
|
3904
|
+
responseThreads.push(thread);
|
|
3905
|
+
} catch (error) {
|
|
3906
|
+
if (error instanceof Error && error.message.includes("Max depth")) {
|
|
3907
|
+
continue;
|
|
3908
|
+
}
|
|
3909
|
+
throw error;
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
return {
|
|
3913
|
+
feedback,
|
|
3914
|
+
responses: responseThreads
|
|
3915
|
+
};
|
|
3916
|
+
}
|
|
3030
3917
|
};
|
|
3031
3918
|
|
|
3032
3919
|
// src/adapters/execution_adapter/index.ts
|
|
@@ -3117,16 +4004,7 @@ var ExecutionAdapter = class {
|
|
|
3117
4004
|
* Returns: Complete and signed ExecutionRecord.
|
|
3118
4005
|
*/
|
|
3119
4006
|
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) {
|
|
4007
|
+
if (this.taskStore && payload.taskId) {
|
|
3130
4008
|
const taskExists = await this.taskStore.read(payload.taskId);
|
|
3131
4009
|
if (!taskExists) {
|
|
3132
4010
|
throw new Error(`RecordNotFoundError: Task not found: ${payload.taskId}`);
|
|
@@ -3142,9 +4020,9 @@ var ExecutionAdapter = class {
|
|
|
3142
4020
|
signatures: [{
|
|
3143
4021
|
keyId: actorId,
|
|
3144
4022
|
role: "author",
|
|
4023
|
+
notes: "Execution recorded",
|
|
3145
4024
|
signature: "placeholder",
|
|
3146
|
-
timestamp: Date.now()
|
|
3147
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
4025
|
+
timestamp: Date.now()
|
|
3148
4026
|
}]
|
|
3149
4027
|
},
|
|
3150
4028
|
payload: validatedPayload
|
|
@@ -3164,9 +4042,6 @@ var ExecutionAdapter = class {
|
|
|
3164
4042
|
});
|
|
3165
4043
|
return validatedPayload;
|
|
3166
4044
|
} catch (error) {
|
|
3167
|
-
if (error instanceof Error && error.message.includes("DetailedValidationError")) {
|
|
3168
|
-
throw error;
|
|
3169
|
-
}
|
|
3170
4045
|
throw error;
|
|
3171
4046
|
}
|
|
3172
4047
|
}
|
|
@@ -3280,31 +4155,21 @@ async function validateFullChangelogRecord(record, getPublicKey) {
|
|
|
3280
4155
|
// src/factories/changelog_factory.ts
|
|
3281
4156
|
async function createChangelogRecord(payload) {
|
|
3282
4157
|
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
4158
|
const changelog = {
|
|
3288
4159
|
// Required fields
|
|
3289
|
-
id: id || "",
|
|
3290
|
-
entityType: payload.entityType || "task",
|
|
3291
|
-
entityId: payload.entityId || "",
|
|
3292
|
-
changeType: payload.changeType || "completion",
|
|
4160
|
+
id: payload.id || "",
|
|
3293
4161
|
title: payload.title || "",
|
|
3294
4162
|
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 },
|
|
4163
|
+
relatedTasks: payload.relatedTasks || [],
|
|
4164
|
+
completedAt: payload.completedAt || timestamp,
|
|
4165
|
+
// Optional fields (only include if provided)
|
|
4166
|
+
...payload.relatedCycles && { relatedCycles: payload.relatedCycles },
|
|
4167
|
+
...payload.relatedExecutions && { relatedExecutions: payload.relatedExecutions },
|
|
4168
|
+
...payload.version && { version: payload.version },
|
|
4169
|
+
...payload.tags && { tags: payload.tags },
|
|
3305
4170
|
...payload.commits && { commits: payload.commits },
|
|
3306
|
-
...payload.
|
|
3307
|
-
...payload.
|
|
4171
|
+
...payload.files && { files: payload.files },
|
|
4172
|
+
...payload.notes && { notes: payload.notes }
|
|
3308
4173
|
};
|
|
3309
4174
|
const validation = validateChangelogRecordDetailed(changelog);
|
|
3310
4175
|
if (!validation.isValid) {
|
|
@@ -3328,54 +4193,44 @@ var ChangelogAdapter = class {
|
|
|
3328
4193
|
this.cycleStore = dependencies.cycleStore;
|
|
3329
4194
|
}
|
|
3330
4195
|
/**
|
|
3331
|
-
* [EARS-1] Records a
|
|
4196
|
+
* [EARS-1] Records a deliverable/release note.
|
|
3332
4197
|
*
|
|
3333
|
-
* Description:
|
|
3334
|
-
* Implementation: Validates
|
|
3335
|
-
* Usage: Invoked by `gitgov changelog add` to document
|
|
3336
|
-
* Returns: Complete and signed ChangelogRecord
|
|
4198
|
+
* Description: Aggregates multiple tasks into a single deliverable/release note.
|
|
4199
|
+
* Implementation: Validates required fields, builds record with factory, signs, persists and emits event.
|
|
4200
|
+
* Usage: Invoked by `gitgov changelog add` to document deliverables.
|
|
4201
|
+
* Returns: Complete and signed ChangelogRecord.
|
|
3337
4202
|
*/
|
|
3338
4203
|
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");
|
|
4204
|
+
if (!payload.title || payload.title.length < 10) {
|
|
4205
|
+
throw new Error("DetailedValidationError: title is required and must be at least 10 characters");
|
|
3350
4206
|
}
|
|
3351
|
-
if (payload.
|
|
3352
|
-
throw new Error("DetailedValidationError:
|
|
4207
|
+
if (!payload.description || payload.description.length < 20) {
|
|
4208
|
+
throw new Error("DetailedValidationError: description is required and must be at least 20 characters");
|
|
3353
4209
|
}
|
|
3354
|
-
if (payload.
|
|
3355
|
-
throw new Error("DetailedValidationError:
|
|
4210
|
+
if (!payload.relatedTasks || payload.relatedTasks.length === 0) {
|
|
4211
|
+
throw new Error("DetailedValidationError: relatedTasks is required and must contain at least one task ID");
|
|
3356
4212
|
}
|
|
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}`);
|
|
4213
|
+
if (this.taskStore && payload.relatedTasks) {
|
|
4214
|
+
for (const taskId of payload.relatedTasks) {
|
|
4215
|
+
const taskExists = await this.taskStore.read(taskId);
|
|
4216
|
+
if (!taskExists) {
|
|
4217
|
+
throw new Error(`RecordNotFoundError: Task not found: ${taskId}`);
|
|
4218
|
+
}
|
|
3370
4219
|
}
|
|
3371
4220
|
}
|
|
3372
|
-
if (
|
|
3373
|
-
const
|
|
3374
|
-
|
|
3375
|
-
|
|
4221
|
+
if (this.cycleStore && payload.relatedCycles) {
|
|
4222
|
+
for (const cycleId of payload.relatedCycles) {
|
|
4223
|
+
const cycleExists = await this.cycleStore.read(cycleId);
|
|
4224
|
+
if (!cycleExists) {
|
|
4225
|
+
throw new Error(`RecordNotFoundError: Cycle not found: ${cycleId}`);
|
|
4226
|
+
}
|
|
3376
4227
|
}
|
|
3377
4228
|
}
|
|
3378
4229
|
try {
|
|
4230
|
+
const timestamp = payload.completedAt || Math.floor(Date.now() / 1e3);
|
|
4231
|
+
if (!payload.id) {
|
|
4232
|
+
payload.id = generateChangelogId(payload.title, timestamp);
|
|
4233
|
+
}
|
|
3379
4234
|
const validatedPayload = await createChangelogRecord(payload);
|
|
3380
4235
|
const unsignedRecord = {
|
|
3381
4236
|
header: {
|
|
@@ -3385,9 +4240,9 @@ var ChangelogAdapter = class {
|
|
|
3385
4240
|
signatures: [{
|
|
3386
4241
|
keyId: actorId,
|
|
3387
4242
|
role: "author",
|
|
4243
|
+
notes: "Changelog entry created",
|
|
3388
4244
|
signature: "placeholder",
|
|
3389
|
-
timestamp: Date.now()
|
|
3390
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
4245
|
+
timestamp: Date.now()
|
|
3391
4246
|
}]
|
|
3392
4247
|
},
|
|
3393
4248
|
payload: validatedPayload
|
|
@@ -3400,13 +4255,9 @@ var ChangelogAdapter = class {
|
|
|
3400
4255
|
source: "changelog_adapter",
|
|
3401
4256
|
payload: {
|
|
3402
4257
|
changelogId: validatedPayload.id,
|
|
3403
|
-
|
|
3404
|
-
entityType: validatedPayload.entityType,
|
|
3405
|
-
changeType: validatedPayload.changeType,
|
|
3406
|
-
triggeredBy: actorId,
|
|
3407
|
-
riskLevel: validatedPayload.riskLevel,
|
|
4258
|
+
relatedTasks: validatedPayload.relatedTasks,
|
|
3408
4259
|
title: validatedPayload.title,
|
|
3409
|
-
|
|
4260
|
+
version: validatedPayload.version
|
|
3410
4261
|
}
|
|
3411
4262
|
});
|
|
3412
4263
|
return validatedPayload;
|
|
@@ -3418,70 +4269,78 @@ var ChangelogAdapter = class {
|
|
|
3418
4269
|
}
|
|
3419
4270
|
}
|
|
3420
4271
|
/**
|
|
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.
|
|
4272
|
+
* [EARS-9] Gets a specific ChangelogRecord by its ID.
|
|
3427
4273
|
*/
|
|
3428
4274
|
async getChangelog(changelogId) {
|
|
3429
4275
|
const record = await this.changelogStore.read(changelogId);
|
|
3430
4276
|
return record ? record.payload : null;
|
|
3431
4277
|
}
|
|
3432
4278
|
/**
|
|
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.
|
|
4279
|
+
* [EARS-11] Gets all ChangelogRecords that include a specific task.
|
|
3439
4280
|
*/
|
|
3440
|
-
async
|
|
4281
|
+
async getChangelogsByTask(taskId) {
|
|
3441
4282
|
const ids = await this.changelogStore.list();
|
|
3442
4283
|
const changelogs = [];
|
|
3443
4284
|
for (const id of ids) {
|
|
3444
4285
|
const record = await this.changelogStore.read(id);
|
|
3445
|
-
if (record && record.payload.
|
|
3446
|
-
|
|
3447
|
-
changelogs.push(record.payload);
|
|
3448
|
-
}
|
|
4286
|
+
if (record && record.payload.relatedTasks.includes(taskId)) {
|
|
4287
|
+
changelogs.push(record.payload);
|
|
3449
4288
|
}
|
|
3450
4289
|
}
|
|
3451
4290
|
return changelogs;
|
|
3452
4291
|
}
|
|
3453
4292
|
/**
|
|
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.
|
|
4293
|
+
* [EARS-11, EARS-12, EARS-13] Gets all ChangelogRecords with optional filtering and sorting.
|
|
3460
4294
|
*/
|
|
3461
|
-
async getAllChangelogs() {
|
|
4295
|
+
async getAllChangelogs(options) {
|
|
3462
4296
|
const ids = await this.changelogStore.list();
|
|
3463
|
-
|
|
4297
|
+
let changelogs = [];
|
|
3464
4298
|
for (const id of ids) {
|
|
3465
4299
|
const record = await this.changelogStore.read(id);
|
|
3466
4300
|
if (record) {
|
|
3467
4301
|
changelogs.push(record.payload);
|
|
3468
4302
|
}
|
|
3469
4303
|
}
|
|
4304
|
+
if (options?.tags && options.tags.length > 0) {
|
|
4305
|
+
changelogs = changelogs.filter((changelog) => {
|
|
4306
|
+
if (!changelog.tags) return false;
|
|
4307
|
+
return options.tags.some((tag) => changelog.tags.includes(tag));
|
|
4308
|
+
});
|
|
4309
|
+
}
|
|
4310
|
+
if (options?.version) {
|
|
4311
|
+
changelogs = changelogs.filter((changelog) => changelog.version === options.version);
|
|
4312
|
+
}
|
|
4313
|
+
const sortBy = options?.sortBy || "completedAt";
|
|
4314
|
+
const sortOrder = options?.sortOrder || "desc";
|
|
4315
|
+
changelogs.sort((a, b) => {
|
|
4316
|
+
let compareValue = 0;
|
|
4317
|
+
if (sortBy === "completedAt") {
|
|
4318
|
+
compareValue = a.completedAt - b.completedAt;
|
|
4319
|
+
} else if (sortBy === "title") {
|
|
4320
|
+
compareValue = a.title.localeCompare(b.title);
|
|
4321
|
+
}
|
|
4322
|
+
return sortOrder === "asc" ? compareValue : -compareValue;
|
|
4323
|
+
});
|
|
4324
|
+
if (options?.limit && options.limit > 0) {
|
|
4325
|
+
changelogs = changelogs.slice(0, options.limit);
|
|
4326
|
+
}
|
|
3470
4327
|
return changelogs;
|
|
3471
4328
|
}
|
|
3472
4329
|
/**
|
|
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.
|
|
4330
|
+
* [EARS-13] Gets recent ChangelogRecords ordered by completedAt.
|
|
3479
4331
|
*/
|
|
3480
4332
|
async getRecentChangelogs(limit) {
|
|
3481
4333
|
const allChangelogs = await this.getAllChangelogs();
|
|
3482
|
-
const sortedChangelogs = allChangelogs.sort((a, b) => b.
|
|
4334
|
+
const sortedChangelogs = allChangelogs.sort((a, b) => b.completedAt - a.completedAt);
|
|
3483
4335
|
return sortedChangelogs.slice(0, limit);
|
|
3484
4336
|
}
|
|
4337
|
+
/**
|
|
4338
|
+
* Legacy method for backwards compatibility - maps to getChangelogsByTask
|
|
4339
|
+
* @deprecated Use getChangelogsByTask instead
|
|
4340
|
+
*/
|
|
4341
|
+
async getChangelogsByEntity(entityId, _entityType) {
|
|
4342
|
+
return this.getChangelogsByTask(entityId);
|
|
4343
|
+
}
|
|
3485
4344
|
};
|
|
3486
4345
|
|
|
3487
4346
|
// src/adapters/metrics_adapter/index.ts
|
|
@@ -3555,13 +4414,17 @@ var MetricsAdapter = class {
|
|
|
3555
4414
|
}
|
|
3556
4415
|
const task = taskRecord.payload;
|
|
3557
4416
|
let feedbacks = [];
|
|
4417
|
+
let allFeedbacks = [];
|
|
3558
4418
|
let executions = [];
|
|
3559
4419
|
if (this.feedbackStore) {
|
|
3560
4420
|
const feedbackIds = await this.feedbackStore.list();
|
|
3561
4421
|
for (const id of feedbackIds) {
|
|
3562
4422
|
const record = await this.feedbackStore.read(id);
|
|
3563
|
-
if (record
|
|
3564
|
-
|
|
4423
|
+
if (record) {
|
|
4424
|
+
allFeedbacks.push(record.payload);
|
|
4425
|
+
if (record.payload.entityId === taskId) {
|
|
4426
|
+
feedbacks.push(record.payload);
|
|
4427
|
+
}
|
|
3565
4428
|
}
|
|
3566
4429
|
}
|
|
3567
4430
|
}
|
|
@@ -3576,7 +4439,13 @@ var MetricsAdapter = class {
|
|
|
3576
4439
|
}
|
|
3577
4440
|
const timeInCurrentStage = this.calculateTimeInCurrentStage(task);
|
|
3578
4441
|
const stalenessIndex = this.calculateStalenessIndex([task]);
|
|
3579
|
-
const blockingFeedbacks = feedbacks.filter((f) =>
|
|
4442
|
+
const blockingFeedbacks = feedbacks.filter((f) => {
|
|
4443
|
+
if (f.type !== "blocking" || f.status !== "open") return false;
|
|
4444
|
+
const hasResolution = allFeedbacks.some(
|
|
4445
|
+
(resolution) => resolution.entityType === "feedback" && resolution.resolvesFeedbackId === f.id && resolution.status === "resolved"
|
|
4446
|
+
);
|
|
4447
|
+
return !hasResolution;
|
|
4448
|
+
}).length;
|
|
3580
4449
|
const lastActivity = executions.length > 0 ? Math.max(...executions.map((e) => this.getTimestampFromId(e.id))) : this.getTimestampFromId(task.id);
|
|
3581
4450
|
const recommendations = [];
|
|
3582
4451
|
if (timeInCurrentStage > 7) recommendations.push("Task has been stagnant for over 7 days");
|
|
@@ -4034,10 +4903,6 @@ var BacklogAdapter = class {
|
|
|
4034
4903
|
"feedback.created",
|
|
4035
4904
|
(event) => this.handleFeedbackCreated(event)
|
|
4036
4905
|
);
|
|
4037
|
-
this.eventBus.subscribe(
|
|
4038
|
-
"feedback.status.changed",
|
|
4039
|
-
(event) => this.handleFeedbackResolved(event)
|
|
4040
|
-
);
|
|
4041
4906
|
this.eventBus.subscribe(
|
|
4042
4907
|
"execution.created",
|
|
4043
4908
|
(event) => this.handleExecutionCreated(event)
|
|
@@ -4069,9 +4934,9 @@ var BacklogAdapter = class {
|
|
|
4069
4934
|
signatures: [{
|
|
4070
4935
|
keyId: actorId,
|
|
4071
4936
|
role: "author",
|
|
4937
|
+
notes: "Task created",
|
|
4072
4938
|
signature: "placeholder",
|
|
4073
|
-
timestamp: Date.now()
|
|
4074
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
4939
|
+
timestamp: Date.now()
|
|
4075
4940
|
}]
|
|
4076
4941
|
},
|
|
4077
4942
|
payload: validatedPayload
|
|
@@ -4174,9 +5039,9 @@ var BacklogAdapter = class {
|
|
|
4174
5039
|
const tempSignature = {
|
|
4175
5040
|
keyId: actorId,
|
|
4176
5041
|
role: "approver",
|
|
5042
|
+
notes: "Task approval",
|
|
4177
5043
|
signature: "temp-signature",
|
|
4178
|
-
timestamp: Date.now()
|
|
4179
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
5044
|
+
timestamp: Date.now()
|
|
4180
5045
|
};
|
|
4181
5046
|
const context = {
|
|
4182
5047
|
task,
|
|
@@ -4507,7 +5372,16 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4507
5372
|
}
|
|
4508
5373
|
// ===== PHASE 3: EVENT HANDLERS (NEW IMPLEMENTATION) =====
|
|
4509
5374
|
/**
|
|
4510
|
-
* [EARS-31] Handles feedback created events
|
|
5375
|
+
* [EARS-31, EARS-33, EARS-34] Handles feedback created events (Immutable Pattern)
|
|
5376
|
+
*
|
|
5377
|
+
* This handler respects the immutable feedback pattern:
|
|
5378
|
+
* - Case 1: Blocking feedback created → pause task if active/ready
|
|
5379
|
+
* - Case 2: Feedback resolving another feedback → resume task if no more blocks
|
|
5380
|
+
*
|
|
5381
|
+
* The immutable pattern means:
|
|
5382
|
+
* - Original feedbacks NEVER change status
|
|
5383
|
+
* - Resolution is expressed by creating a NEW feedback pointing to the original
|
|
5384
|
+
* - We detect resolution via: entityType='feedback' + status='resolved' + resolvesFeedbackId
|
|
4511
5385
|
*/
|
|
4512
5386
|
async handleFeedbackCreated(event) {
|
|
4513
5387
|
try {
|
|
@@ -4517,85 +5391,72 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4517
5391
|
processedAt: Date.now(),
|
|
4518
5392
|
sourceAdapter: "backlog_adapter"
|
|
4519
5393
|
};
|
|
4520
|
-
if (event.payload.type
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
5394
|
+
if (event.payload.type === "blocking" && event.payload.entityType === "task") {
|
|
5395
|
+
const task = await this.getTask(event.payload.entityId);
|
|
5396
|
+
if (!task) {
|
|
5397
|
+
console.warn(`Task not found for feedback: ${event.payload.entityId}`);
|
|
5398
|
+
return;
|
|
5399
|
+
}
|
|
5400
|
+
if (!["active", "ready"].includes(task.status)) {
|
|
5401
|
+
return;
|
|
5402
|
+
}
|
|
5403
|
+
const updatedTask = { ...task, status: "paused" };
|
|
5404
|
+
const taskRecord = await this.taskStore.read(task.id);
|
|
5405
|
+
if (taskRecord) {
|
|
5406
|
+
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
5407
|
+
await this.taskStore.write(updatedRecord);
|
|
5408
|
+
this.eventBus.publish({
|
|
5409
|
+
type: "task.status.changed",
|
|
5410
|
+
timestamp: Date.now(),
|
|
5411
|
+
source: "backlog_adapter",
|
|
5412
|
+
payload: {
|
|
5413
|
+
taskId: task.id,
|
|
5414
|
+
oldStatus: task.status,
|
|
5415
|
+
newStatus: "paused",
|
|
5416
|
+
actorId: "system"
|
|
5417
|
+
},
|
|
5418
|
+
metadata
|
|
5419
|
+
});
|
|
5420
|
+
}
|
|
4531
5421
|
return;
|
|
4532
5422
|
}
|
|
4533
|
-
if (
|
|
5423
|
+
if (event.payload.entityType === "feedback" && event.payload.status === "resolved" && event.payload.resolvesFeedbackId) {
|
|
5424
|
+
const originalFeedback = await this.feedbackAdapter.getFeedback(event.payload.resolvesFeedbackId);
|
|
5425
|
+
if (!originalFeedback || originalFeedback.type !== "blocking") {
|
|
5426
|
+
return;
|
|
5427
|
+
}
|
|
5428
|
+
const task = await this.getTask(originalFeedback.entityId);
|
|
5429
|
+
if (!task || task.status !== "paused") {
|
|
5430
|
+
return;
|
|
5431
|
+
}
|
|
5432
|
+
const taskHealth = await this.metricsAdapter.getTaskHealth(task.id);
|
|
5433
|
+
if (taskHealth.blockingFeedbacks > 0) {
|
|
5434
|
+
return;
|
|
5435
|
+
}
|
|
5436
|
+
const updatedTask = { ...task, status: "active" };
|
|
5437
|
+
const taskRecord = await this.taskStore.read(task.id);
|
|
5438
|
+
if (taskRecord) {
|
|
5439
|
+
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
5440
|
+
await this.taskStore.write(updatedRecord);
|
|
5441
|
+
this.eventBus.publish({
|
|
5442
|
+
type: "task.status.changed",
|
|
5443
|
+
timestamp: Date.now(),
|
|
5444
|
+
source: "backlog_adapter",
|
|
5445
|
+
payload: {
|
|
5446
|
+
taskId: task.id,
|
|
5447
|
+
oldStatus: "paused",
|
|
5448
|
+
newStatus: "active",
|
|
5449
|
+
actorId: "system"
|
|
5450
|
+
},
|
|
5451
|
+
metadata
|
|
5452
|
+
});
|
|
5453
|
+
}
|
|
4534
5454
|
return;
|
|
4535
5455
|
}
|
|
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
5456
|
} catch (error) {
|
|
4555
5457
|
console.error("Error in handleFeedbackCreated:", error);
|
|
4556
5458
|
}
|
|
4557
5459
|
}
|
|
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
5460
|
/**
|
|
4600
5461
|
* [EARS-35] Handles execution created events - transitions ready→active on first execution
|
|
4601
5462
|
*/
|
|
@@ -4649,29 +5510,31 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4649
5510
|
console.warn(`Changelog not found: ${event.payload.changelogId}`);
|
|
4650
5511
|
return;
|
|
4651
5512
|
}
|
|
4652
|
-
if (changelogRecord.payload.
|
|
4653
|
-
return;
|
|
4654
|
-
}
|
|
4655
|
-
const task = await this.getTask(changelogRecord.payload.entityId);
|
|
4656
|
-
if (!task || task.status !== "done") {
|
|
5513
|
+
if (!changelogRecord.payload.relatedTasks || changelogRecord.payload.relatedTasks.length === 0) {
|
|
4657
5514
|
return;
|
|
4658
5515
|
}
|
|
4659
|
-
const
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
5516
|
+
for (const taskId of changelogRecord.payload.relatedTasks) {
|
|
5517
|
+
const task = await this.getTask(taskId);
|
|
5518
|
+
if (!task || task.status !== "done") {
|
|
5519
|
+
continue;
|
|
5520
|
+
}
|
|
5521
|
+
const updatedTask = { ...task, status: "archived" };
|
|
5522
|
+
const taskRecord = await this.taskStore.read(task.id);
|
|
5523
|
+
if (taskRecord) {
|
|
5524
|
+
const updatedRecord = { ...taskRecord, payload: updatedTask };
|
|
5525
|
+
await this.taskStore.write(updatedRecord);
|
|
5526
|
+
this.eventBus.publish({
|
|
5527
|
+
type: "task.status.changed",
|
|
5528
|
+
timestamp: Date.now(),
|
|
5529
|
+
source: "backlog_adapter",
|
|
5530
|
+
payload: {
|
|
5531
|
+
taskId: task.id,
|
|
5532
|
+
oldStatus: "done",
|
|
5533
|
+
newStatus: "archived",
|
|
5534
|
+
actorId: "system"
|
|
5535
|
+
}
|
|
5536
|
+
});
|
|
5537
|
+
}
|
|
4675
5538
|
}
|
|
4676
5539
|
} catch (error) {
|
|
4677
5540
|
console.error("Error in handleChangelogCreated:", error);
|
|
@@ -4785,9 +5648,9 @@ ${task.status === "review" ? "[REJECTED]" : "[CANCELLED]"} ${reason} (${(/* @__P
|
|
|
4785
5648
|
signatures: [{
|
|
4786
5649
|
keyId: actorId,
|
|
4787
5650
|
role: "author",
|
|
5651
|
+
notes: "Cycle created",
|
|
4788
5652
|
signature: "placeholder",
|
|
4789
|
-
timestamp: Date.now()
|
|
4790
|
-
timestamp_iso: (/* @__PURE__ */ new Date()).toISOString()
|
|
5653
|
+
timestamp: Date.now()
|
|
4791
5654
|
}]
|
|
4792
5655
|
},
|
|
4793
5656
|
payload: validatedPayload
|
|
@@ -5146,11 +6009,14 @@ var FileIndexerAdapter = class {
|
|
|
5146
6009
|
metrics: { ...systemStatus, ...productivityMetrics, ...collaborationMetrics },
|
|
5147
6010
|
activityHistory,
|
|
5148
6011
|
// NUEVO
|
|
5149
|
-
tasks,
|
|
6012
|
+
tasks: tasks.map((t) => t.payload),
|
|
6013
|
+
// Extract payloads for IndexData
|
|
5150
6014
|
enrichedTasks,
|
|
5151
6015
|
// NUEVO - Tasks with activity metadata
|
|
5152
|
-
cycles,
|
|
5153
|
-
|
|
6016
|
+
cycles: cycles.map((c) => c.payload),
|
|
6017
|
+
// Extract payloads for IndexData
|
|
6018
|
+
actors: actors.map((a) => a.payload)
|
|
6019
|
+
// Extract payloads for IndexData
|
|
5154
6020
|
};
|
|
5155
6021
|
const writeStart = performance.now();
|
|
5156
6022
|
await this.writeCacheFile(indexData);
|
|
@@ -5218,19 +6084,19 @@ var FileIndexerAdapter = class {
|
|
|
5218
6084
|
]);
|
|
5219
6085
|
recordsScanned = tasks.length + cycles.length;
|
|
5220
6086
|
for (const task of tasks) {
|
|
5221
|
-
if (!task.id || !task.description) {
|
|
6087
|
+
if (!task.payload.id || !task.payload.description) {
|
|
5222
6088
|
errors.push({
|
|
5223
6089
|
type: "schema_violation",
|
|
5224
|
-
recordId: task.id || "unknown",
|
|
6090
|
+
recordId: task.payload.id || "unknown",
|
|
5225
6091
|
message: "Task missing required fields"
|
|
5226
6092
|
});
|
|
5227
6093
|
}
|
|
5228
6094
|
}
|
|
5229
6095
|
for (const cycle of cycles) {
|
|
5230
|
-
if (!cycle.id || !cycle.title) {
|
|
6096
|
+
if (!cycle.payload.id || !cycle.payload.title) {
|
|
5231
6097
|
errors.push({
|
|
5232
6098
|
type: "schema_violation",
|
|
5233
|
-
recordId: cycle.id || "unknown",
|
|
6099
|
+
recordId: cycle.payload.id || "unknown",
|
|
5234
6100
|
message: "Cycle missing required fields"
|
|
5235
6101
|
});
|
|
5236
6102
|
}
|
|
@@ -5303,7 +6169,8 @@ var FileIndexerAdapter = class {
|
|
|
5303
6169
|
}
|
|
5304
6170
|
// ===== HELPER METHODS =====
|
|
5305
6171
|
/**
|
|
5306
|
-
* Reads all tasks from taskStore
|
|
6172
|
+
* Reads all tasks from taskStore with full metadata (headers + payloads).
|
|
6173
|
+
* Returns complete GitGovTaskRecord objects including signatures for author/lastModifier extraction.
|
|
5307
6174
|
*/
|
|
5308
6175
|
async readAllTasks() {
|
|
5309
6176
|
const taskIds = await this.taskStore.list();
|
|
@@ -5311,13 +6178,13 @@ var FileIndexerAdapter = class {
|
|
|
5311
6178
|
for (const id of taskIds) {
|
|
5312
6179
|
const record = await this.taskStore.read(id);
|
|
5313
6180
|
if (record) {
|
|
5314
|
-
tasks.push(record
|
|
6181
|
+
tasks.push(record);
|
|
5315
6182
|
}
|
|
5316
6183
|
}
|
|
5317
6184
|
return tasks;
|
|
5318
6185
|
}
|
|
5319
6186
|
/**
|
|
5320
|
-
* Reads all cycles from cycleStore
|
|
6187
|
+
* Reads all cycles from cycleStore with full metadata.
|
|
5321
6188
|
*/
|
|
5322
6189
|
async readAllCycles() {
|
|
5323
6190
|
const cycleIds = await this.cycleStore.list();
|
|
@@ -5325,13 +6192,13 @@ var FileIndexerAdapter = class {
|
|
|
5325
6192
|
for (const id of cycleIds) {
|
|
5326
6193
|
const record = await this.cycleStore.read(id);
|
|
5327
6194
|
if (record) {
|
|
5328
|
-
cycles.push(record
|
|
6195
|
+
cycles.push(record);
|
|
5329
6196
|
}
|
|
5330
6197
|
}
|
|
5331
6198
|
return cycles;
|
|
5332
6199
|
}
|
|
5333
6200
|
/**
|
|
5334
|
-
* Reads all actors from actorStore (graceful degradation)
|
|
6201
|
+
* Reads all actors from actorStore (graceful degradation) with full metadata.
|
|
5335
6202
|
*/
|
|
5336
6203
|
async readAllActors() {
|
|
5337
6204
|
if (!this.actorStore) {
|
|
@@ -5342,13 +6209,13 @@ var FileIndexerAdapter = class {
|
|
|
5342
6209
|
for (const id of actorIds) {
|
|
5343
6210
|
const record = await this.actorStore.read(id);
|
|
5344
6211
|
if (record) {
|
|
5345
|
-
actors.push(record
|
|
6212
|
+
actors.push(record);
|
|
5346
6213
|
}
|
|
5347
6214
|
}
|
|
5348
6215
|
return actors;
|
|
5349
6216
|
}
|
|
5350
6217
|
/**
|
|
5351
|
-
* Reads all feedback from feedbackStore (graceful degradation)
|
|
6218
|
+
* Reads all feedback from feedbackStore (graceful degradation) with full metadata.
|
|
5352
6219
|
*/
|
|
5353
6220
|
async readAllFeedback() {
|
|
5354
6221
|
if (!this.feedbackStore) {
|
|
@@ -5359,13 +6226,13 @@ var FileIndexerAdapter = class {
|
|
|
5359
6226
|
for (const id of feedbackIds) {
|
|
5360
6227
|
const record = await this.feedbackStore.read(id);
|
|
5361
6228
|
if (record) {
|
|
5362
|
-
feedback.push(record
|
|
6229
|
+
feedback.push(record);
|
|
5363
6230
|
}
|
|
5364
6231
|
}
|
|
5365
6232
|
return feedback;
|
|
5366
6233
|
}
|
|
5367
6234
|
/**
|
|
5368
|
-
* Reads all executions from executionStore (graceful degradation)
|
|
6235
|
+
* Reads all executions from executionStore (graceful degradation) with full metadata.
|
|
5369
6236
|
*/
|
|
5370
6237
|
async readAllExecutions() {
|
|
5371
6238
|
if (!this.executionStore) {
|
|
@@ -5376,13 +6243,13 @@ var FileIndexerAdapter = class {
|
|
|
5376
6243
|
for (const id of executionIds) {
|
|
5377
6244
|
const record = await this.executionStore.read(id);
|
|
5378
6245
|
if (record) {
|
|
5379
|
-
executions.push(record
|
|
6246
|
+
executions.push(record);
|
|
5380
6247
|
}
|
|
5381
6248
|
}
|
|
5382
6249
|
return executions;
|
|
5383
6250
|
}
|
|
5384
6251
|
/**
|
|
5385
|
-
* Reads all changelogs from changelogStore (graceful degradation)
|
|
6252
|
+
* Reads all changelogs from changelogStore (graceful degradation) with full metadata.
|
|
5386
6253
|
*/
|
|
5387
6254
|
async readAllChangelogs() {
|
|
5388
6255
|
if (!this.changelogStore) {
|
|
@@ -5393,7 +6260,7 @@ var FileIndexerAdapter = class {
|
|
|
5393
6260
|
for (const id of changelogIds) {
|
|
5394
6261
|
const record = await this.changelogStore.read(id);
|
|
5395
6262
|
if (record) {
|
|
5396
|
-
changelogs.push(record
|
|
6263
|
+
changelogs.push(record);
|
|
5397
6264
|
}
|
|
5398
6265
|
}
|
|
5399
6266
|
return changelogs;
|
|
@@ -5402,7 +6269,7 @@ var FileIndexerAdapter = class {
|
|
|
5402
6269
|
* Writes cache data to file (Phase 1: JSON)
|
|
5403
6270
|
*/
|
|
5404
6271
|
async writeCacheFile(indexData) {
|
|
5405
|
-
const cacheDir =
|
|
6272
|
+
const cacheDir = path.dirname(this.cachePath);
|
|
5406
6273
|
await promises.mkdir(cacheDir, { recursive: true });
|
|
5407
6274
|
const jsonContent = JSON.stringify(indexData, null, 2);
|
|
5408
6275
|
await promises.writeFile(this.cachePath, jsonContent, "utf-8");
|
|
@@ -5446,96 +6313,96 @@ var FileIndexerAdapter = class {
|
|
|
5446
6313
|
const events = [];
|
|
5447
6314
|
try {
|
|
5448
6315
|
allRecords.tasks.forEach((task) => {
|
|
5449
|
-
const timestampPart = task.id.split("-")[0];
|
|
6316
|
+
const timestampPart = task.payload.id.split("-")[0];
|
|
5450
6317
|
if (timestampPart) {
|
|
5451
6318
|
events.push({
|
|
5452
6319
|
timestamp: parseInt(timestampPart),
|
|
5453
6320
|
type: "task_created",
|
|
5454
|
-
entityId: task.id,
|
|
5455
|
-
entityTitle: task.title,
|
|
5456
|
-
actorId: "
|
|
5457
|
-
//
|
|
5458
|
-
metadata: { priority: task.priority, status: task.status }
|
|
6321
|
+
entityId: task.payload.id,
|
|
6322
|
+
entityTitle: task.payload.title,
|
|
6323
|
+
actorId: task.header.signatures[0]?.keyId || "unknown",
|
|
6324
|
+
// Extract from first signature
|
|
6325
|
+
metadata: { priority: task.payload.priority, status: task.payload.status }
|
|
5459
6326
|
});
|
|
5460
6327
|
}
|
|
5461
6328
|
});
|
|
5462
6329
|
allRecords.cycles.forEach((cycle) => {
|
|
5463
|
-
const timestampPart = cycle.id.split("-")[0];
|
|
6330
|
+
const timestampPart = cycle.payload.id.split("-")[0];
|
|
5464
6331
|
if (timestampPart) {
|
|
5465
6332
|
events.push({
|
|
5466
6333
|
timestamp: parseInt(timestampPart),
|
|
5467
6334
|
type: "cycle_created",
|
|
5468
|
-
entityId: cycle.id,
|
|
5469
|
-
entityTitle: cycle.title,
|
|
5470
|
-
actorId: "
|
|
5471
|
-
//
|
|
5472
|
-
metadata: { status: cycle.status }
|
|
6335
|
+
entityId: cycle.payload.id,
|
|
6336
|
+
entityTitle: cycle.payload.title,
|
|
6337
|
+
actorId: cycle.header.signatures[0]?.keyId || "unknown",
|
|
6338
|
+
// Extract from first signature
|
|
6339
|
+
metadata: { status: cycle.payload.status }
|
|
5473
6340
|
});
|
|
5474
6341
|
}
|
|
5475
6342
|
});
|
|
5476
6343
|
allRecords.feedback.forEach((feedback) => {
|
|
5477
|
-
const timestampPart = feedback.id.split("-")[0];
|
|
6344
|
+
const timestampPart = feedback.payload.id.split("-")[0];
|
|
5478
6345
|
if (timestampPart) {
|
|
5479
6346
|
const metadata = {
|
|
5480
|
-
type: feedback.type,
|
|
5481
|
-
resolution: feedback.status
|
|
6347
|
+
type: feedback.payload.type,
|
|
6348
|
+
resolution: feedback.payload.status
|
|
5482
6349
|
};
|
|
5483
|
-
if (feedback.assignee) {
|
|
5484
|
-
metadata.assignee = feedback.assignee;
|
|
6350
|
+
if (feedback.payload.assignee) {
|
|
6351
|
+
metadata.assignee = feedback.payload.assignee;
|
|
5485
6352
|
}
|
|
5486
6353
|
const event = {
|
|
5487
6354
|
timestamp: parseInt(timestampPart),
|
|
5488
6355
|
type: "feedback_created",
|
|
5489
|
-
entityId: feedback.id,
|
|
5490
|
-
entityTitle: `${feedback.type}: ${feedback.content.slice(0, 40)}...`,
|
|
6356
|
+
entityId: feedback.payload.id,
|
|
6357
|
+
entityTitle: `${feedback.payload.type}: ${feedback.payload.content.slice(0, 40)}...`,
|
|
6358
|
+
actorId: feedback.header.signatures[0]?.keyId || feedback.payload.assignee || "unknown",
|
|
5491
6359
|
metadata
|
|
5492
6360
|
};
|
|
5493
|
-
if (feedback.assignee) {
|
|
5494
|
-
event.actorId = feedback.assignee;
|
|
5495
|
-
}
|
|
5496
6361
|
events.push(event);
|
|
5497
6362
|
}
|
|
5498
6363
|
});
|
|
5499
6364
|
allRecords.changelogs.forEach((changelog) => {
|
|
5500
|
-
const timestampPart = changelog.id.split("-")[0];
|
|
6365
|
+
const timestampPart = changelog.payload.id.split("-")[0];
|
|
5501
6366
|
if (timestampPart) {
|
|
5502
|
-
|
|
6367
|
+
const event = {
|
|
5503
6368
|
timestamp: parseInt(timestampPart),
|
|
5504
6369
|
type: "changelog_created",
|
|
5505
|
-
entityId: changelog.id,
|
|
5506
|
-
entityTitle: changelog.title || "Release notes",
|
|
5507
|
-
actorId: "
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
6370
|
+
entityId: changelog.payload.id,
|
|
6371
|
+
entityTitle: changelog.payload.title || "Release notes",
|
|
6372
|
+
actorId: changelog.header.signatures[0]?.keyId || "unknown"
|
|
6373
|
+
};
|
|
6374
|
+
if (changelog.payload.version) {
|
|
6375
|
+
event.metadata = { version: changelog.payload.version };
|
|
6376
|
+
}
|
|
6377
|
+
events.push(event);
|
|
5511
6378
|
}
|
|
5512
6379
|
});
|
|
5513
6380
|
allRecords.executions.forEach((execution) => {
|
|
5514
|
-
const timestampPart = execution.id.split("-")[0];
|
|
6381
|
+
const timestampPart = execution.payload.id.split("-")[0];
|
|
5515
6382
|
if (timestampPart) {
|
|
5516
6383
|
events.push({
|
|
5517
6384
|
timestamp: parseInt(timestampPart),
|
|
5518
6385
|
type: "execution_created",
|
|
5519
|
-
entityId: execution.id,
|
|
5520
|
-
entityTitle: execution.title || `Working on ${execution.taskId.slice(-8)}`,
|
|
5521
|
-
actorId: "
|
|
5522
|
-
//
|
|
6386
|
+
entityId: execution.payload.id,
|
|
6387
|
+
entityTitle: execution.payload.title || `Working on ${execution.payload.taskId.slice(-8)}`,
|
|
6388
|
+
actorId: execution.header.signatures[0]?.keyId || "unknown",
|
|
6389
|
+
// Extract from first signature
|
|
5523
6390
|
metadata: {
|
|
5524
|
-
executionType: execution.type || "development",
|
|
5525
|
-
taskId: execution.taskId
|
|
6391
|
+
executionType: execution.payload.type || "development",
|
|
6392
|
+
taskId: execution.payload.taskId
|
|
5526
6393
|
}
|
|
5527
6394
|
});
|
|
5528
6395
|
}
|
|
5529
6396
|
});
|
|
5530
6397
|
allRecords.actors.forEach((actor) => {
|
|
5531
|
-
const timestampPart = actor.id.split("-")[0];
|
|
6398
|
+
const timestampPart = actor.payload.id.split("-")[0];
|
|
5532
6399
|
if (timestampPart) {
|
|
5533
6400
|
events.push({
|
|
5534
6401
|
timestamp: parseInt(timestampPart),
|
|
5535
6402
|
type: "actor_created",
|
|
5536
|
-
entityId: actor.id,
|
|
5537
|
-
entityTitle: `${actor.displayName} joined (${actor.type})`,
|
|
5538
|
-
metadata: { type: actor.type }
|
|
6403
|
+
entityId: actor.payload.id,
|
|
6404
|
+
entityTitle: `${actor.payload.displayName} joined (${actor.payload.type})`,
|
|
6405
|
+
metadata: { type: actor.payload.type }
|
|
5539
6406
|
});
|
|
5540
6407
|
}
|
|
5541
6408
|
});
|
|
@@ -5556,10 +6423,10 @@ var FileIndexerAdapter = class {
|
|
|
5556
6423
|
let recentActivity = "Task created";
|
|
5557
6424
|
try {
|
|
5558
6425
|
let projectRoot2 = process.cwd();
|
|
5559
|
-
while (!fs.existsSync(
|
|
5560
|
-
projectRoot2 =
|
|
6426
|
+
while (!fs.existsSync(path.join(projectRoot2, ".gitgov")) && projectRoot2 !== "/") {
|
|
6427
|
+
projectRoot2 = path.dirname(projectRoot2);
|
|
5561
6428
|
}
|
|
5562
|
-
const taskFilePath =
|
|
6429
|
+
const taskFilePath = path.join(projectRoot2, ".gitgov", "tasks", `${task.id}.json`);
|
|
5563
6430
|
const stats = await promises.stat(taskFilePath);
|
|
5564
6431
|
const fileModTime = stats.mtime.getTime();
|
|
5565
6432
|
const creationTime = this.getTimestampFromId(task.id) * 1e3;
|
|
@@ -5572,34 +6439,34 @@ var FileIndexerAdapter = class {
|
|
|
5572
6439
|
} catch (error) {
|
|
5573
6440
|
}
|
|
5574
6441
|
const relatedFeedback = relatedRecords.feedback.filter(
|
|
5575
|
-
(f) => f.entityId === task.id || f.content.includes(task.id)
|
|
6442
|
+
(f) => f.payload.entityId === task.id || f.payload.content.includes(task.id)
|
|
5576
6443
|
);
|
|
5577
6444
|
for (const feedback of relatedFeedback) {
|
|
5578
|
-
const feedbackTime = this.getTimestampFromId(feedback.id) * 1e3;
|
|
6445
|
+
const feedbackTime = this.getTimestampFromId(feedback.payload.id) * 1e3;
|
|
5579
6446
|
if (feedbackTime > lastUpdated) {
|
|
5580
6447
|
lastUpdated = feedbackTime;
|
|
5581
6448
|
lastActivityType = "feedback_received";
|
|
5582
|
-
recentActivity = `${feedback.type} feedback: ${feedback.content.slice(0, 30)}...`;
|
|
6449
|
+
recentActivity = `${feedback.payload.type} feedback: ${feedback.payload.content.slice(0, 30)}...`;
|
|
5583
6450
|
}
|
|
5584
6451
|
}
|
|
5585
|
-
const relatedExecutions = relatedRecords.executions.filter((e) => e.taskId === task.id);
|
|
6452
|
+
const relatedExecutions = relatedRecords.executions.filter((e) => e.payload.taskId === task.id);
|
|
5586
6453
|
for (const execution of relatedExecutions) {
|
|
5587
|
-
const executionTime = this.getTimestampFromId(execution.id) * 1e3;
|
|
6454
|
+
const executionTime = this.getTimestampFromId(execution.payload.id) * 1e3;
|
|
5588
6455
|
if (executionTime > lastUpdated) {
|
|
5589
6456
|
lastUpdated = executionTime;
|
|
5590
6457
|
lastActivityType = "execution_added";
|
|
5591
|
-
recentActivity = `Execution: ${execution.title || "Work logged"}`;
|
|
6458
|
+
recentActivity = `Execution: ${execution.payload.title || "Work logged"}`;
|
|
5592
6459
|
}
|
|
5593
6460
|
}
|
|
5594
6461
|
const relatedChangelogs = relatedRecords.changelogs.filter(
|
|
5595
|
-
(c) => c.
|
|
6462
|
+
(c) => c.payload.relatedTasks.includes(task.id) || c.payload.description?.includes(task.id)
|
|
5596
6463
|
);
|
|
5597
6464
|
for (const changelog of relatedChangelogs) {
|
|
5598
|
-
const changelogTime = this.getTimestampFromId(changelog.id) * 1e3;
|
|
6465
|
+
const changelogTime = this.getTimestampFromId(changelog.payload.id) * 1e3;
|
|
5599
6466
|
if (changelogTime > lastUpdated) {
|
|
5600
6467
|
lastUpdated = changelogTime;
|
|
5601
6468
|
lastActivityType = "changelog_created";
|
|
5602
|
-
recentActivity = `Changelog: ${changelog.title}`;
|
|
6469
|
+
recentActivity = `Changelog: ${changelog.payload.title}`;
|
|
5603
6470
|
}
|
|
5604
6471
|
}
|
|
5605
6472
|
return { lastUpdated, lastActivityType, recentActivity };
|
|
@@ -5614,11 +6481,13 @@ var FileIndexerAdapter = class {
|
|
|
5614
6481
|
}
|
|
5615
6482
|
/**
|
|
5616
6483
|
* [EARS-22] Enrich a TaskRecord with activity metadata
|
|
6484
|
+
* @param task - Full GitGovTaskRecord with header.signatures for author/lastModifier extraction
|
|
6485
|
+
* @param relatedRecords - All related records with full metadata
|
|
5617
6486
|
*/
|
|
5618
6487
|
async enrichTaskRecord(task, relatedRecords) {
|
|
5619
|
-
const { lastUpdated, lastActivityType, recentActivity } = await this.calculateLastUpdated(task, relatedRecords);
|
|
6488
|
+
const { lastUpdated, lastActivityType, recentActivity } = await this.calculateLastUpdated(task.payload, relatedRecords);
|
|
5620
6489
|
return {
|
|
5621
|
-
...task,
|
|
6490
|
+
...task.payload,
|
|
5622
6491
|
lastUpdated,
|
|
5623
6492
|
lastActivityType,
|
|
5624
6493
|
recentActivity
|
|
@@ -5681,8 +6550,9 @@ var ProjectAdapter = class {
|
|
|
5681
6550
|
throw new Error(`Environment validation failed: ${envValidation.warnings.join(", ")}`);
|
|
5682
6551
|
}
|
|
5683
6552
|
const projectRoot2 = process.env["GITGOV_ORIGINAL_DIR"] || process.cwd();
|
|
5684
|
-
const gitgovPath =
|
|
6553
|
+
const gitgovPath = path.join(projectRoot2, ".gitgov");
|
|
5685
6554
|
await this.createDirectoryStructure(gitgovPath);
|
|
6555
|
+
await this.copyAgentPrompt(gitgovPath);
|
|
5686
6556
|
const actor = await this.identityAdapter.createActor(
|
|
5687
6557
|
{
|
|
5688
6558
|
type: "human",
|
|
@@ -5735,7 +6605,6 @@ var ProjectAdapter = class {
|
|
|
5735
6605
|
await this.persistConfiguration(config, gitgovPath);
|
|
5736
6606
|
await this.initializeSession(actor.id, gitgovPath);
|
|
5737
6607
|
await this.setupGitIntegration(projectRoot2);
|
|
5738
|
-
await this.setupKiroIntegration(projectRoot2);
|
|
5739
6608
|
const initializationTime = Date.now() - startTime;
|
|
5740
6609
|
return {
|
|
5741
6610
|
success: true,
|
|
@@ -5745,7 +6614,7 @@ var ProjectAdapter = class {
|
|
|
5745
6614
|
actor: {
|
|
5746
6615
|
id: actor.id,
|
|
5747
6616
|
displayName: actor.displayName,
|
|
5748
|
-
publicKeyPath:
|
|
6617
|
+
publicKeyPath: path.join(gitgovPath, "actors", `${actor.id}.json`)
|
|
5749
6618
|
},
|
|
5750
6619
|
template: templateResult ? {
|
|
5751
6620
|
processed: true,
|
|
@@ -5772,7 +6641,7 @@ var ProjectAdapter = class {
|
|
|
5772
6641
|
const warnings = [];
|
|
5773
6642
|
const suggestions = [];
|
|
5774
6643
|
try {
|
|
5775
|
-
const gitPath =
|
|
6644
|
+
const gitPath = path.join(targetPath, ".git");
|
|
5776
6645
|
const isGitRepo = existsSync(gitPath);
|
|
5777
6646
|
if (!isGitRepo) {
|
|
5778
6647
|
warnings.push(`Not a Git repository in directory: ${targetPath}`);
|
|
@@ -5780,7 +6649,7 @@ var ProjectAdapter = class {
|
|
|
5780
6649
|
}
|
|
5781
6650
|
let hasWritePermissions = false;
|
|
5782
6651
|
try {
|
|
5783
|
-
const testFile =
|
|
6652
|
+
const testFile = path.join(targetPath, ".gitgov-test");
|
|
5784
6653
|
await promises.writeFile(testFile, "test");
|
|
5785
6654
|
await promises.unlink(testFile);
|
|
5786
6655
|
hasWritePermissions = true;
|
|
@@ -5788,7 +6657,7 @@ var ProjectAdapter = class {
|
|
|
5788
6657
|
warnings.push("No write permissions in target directory");
|
|
5789
6658
|
suggestions.push("Ensure you have write permissions in the target directory");
|
|
5790
6659
|
}
|
|
5791
|
-
const gitgovPath =
|
|
6660
|
+
const gitgovPath = path.join(targetPath, ".gitgov");
|
|
5792
6661
|
let isAlreadyInitialized = false;
|
|
5793
6662
|
try {
|
|
5794
6663
|
await promises.access(gitgovPath);
|
|
@@ -5877,59 +6746,13 @@ var ProjectAdapter = class {
|
|
|
5877
6746
|
]);
|
|
5878
6747
|
}
|
|
5879
6748
|
}
|
|
5880
|
-
/**
|
|
5881
|
-
* Sets up Kiro IDE integration by always copying GitGovernance hooks
|
|
5882
|
-
*/
|
|
5883
|
-
async setupKiroIntegration(projectRoot2) {
|
|
5884
|
-
const kiroDir = pathUtils.join(projectRoot2, ".kiro");
|
|
5885
|
-
const kiroHooksDir = pathUtils.join(kiroDir, "hooks");
|
|
5886
|
-
try {
|
|
5887
|
-
await promises.mkdir(kiroHooksDir, { recursive: true });
|
|
5888
|
-
} catch {
|
|
5889
|
-
}
|
|
5890
|
-
const sourceHooksDir = pathUtils.join(ConfigManager.findProjectRoot() || process.cwd(), ".kiro/hooks");
|
|
5891
|
-
try {
|
|
5892
|
-
await promises.access(sourceHooksDir);
|
|
5893
|
-
const essentialHooks = [
|
|
5894
|
-
"gitgov-auto-indexer.kiro.hook",
|
|
5895
|
-
"git-diagnostics-commit.kiro.hook",
|
|
5896
|
-
"gitgov-file-analyzer.kiro.hook",
|
|
5897
|
-
"code-quality-analyzer.kiro.hook",
|
|
5898
|
-
"gitgov-quick-status.kiro.hook",
|
|
5899
|
-
"gitgov-task-creator.kiro.hook",
|
|
5900
|
-
"gitgov-work-session.kiro.hook"
|
|
5901
|
-
];
|
|
5902
|
-
let copiedHooks = 0;
|
|
5903
|
-
for (const hookFile of essentialHooks) {
|
|
5904
|
-
try {
|
|
5905
|
-
const sourcePath = pathUtils.join(sourceHooksDir, hookFile);
|
|
5906
|
-
const targetPath = pathUtils.join(kiroHooksDir, hookFile);
|
|
5907
|
-
await promises.copyFile(sourcePath, targetPath);
|
|
5908
|
-
copiedHooks++;
|
|
5909
|
-
} catch {
|
|
5910
|
-
}
|
|
5911
|
-
}
|
|
5912
|
-
try {
|
|
5913
|
-
const sourceGitgovPath = pathUtils.join(ConfigManager.findProjectRoot() || process.cwd(), ".gitgov/gitgov");
|
|
5914
|
-
const targetGitgovPath = pathUtils.join(projectRoot2, ".gitgov/gitgov");
|
|
5915
|
-
await promises.copyFile(sourceGitgovPath, targetGitgovPath);
|
|
5916
|
-
await promises.chmod(targetGitgovPath, 493);
|
|
5917
|
-
console.log(`\u{1F4CB} GitGovernance executable copied to .gitgov/gitgov`);
|
|
5918
|
-
} catch {
|
|
5919
|
-
}
|
|
5920
|
-
if (copiedHooks > 0) {
|
|
5921
|
-
console.log(`\u{1F527} Kiro IDE Integration: ${copiedHooks} GitGovernance hooks installed`);
|
|
5922
|
-
}
|
|
5923
|
-
} catch {
|
|
5924
|
-
}
|
|
5925
|
-
}
|
|
5926
6749
|
/**
|
|
5927
6750
|
* [EARS-4] Cleans up partial setup artifacts if initialization fails
|
|
5928
6751
|
*/
|
|
5929
6752
|
async rollbackPartialSetup(setupId) {
|
|
5930
6753
|
try {
|
|
5931
6754
|
const projectRoot2 = process.env["GITGOV_ORIGINAL_DIR"] || process.cwd();
|
|
5932
|
-
const gitgovPath =
|
|
6755
|
+
const gitgovPath = path.join(projectRoot2, ".gitgov");
|
|
5933
6756
|
try {
|
|
5934
6757
|
await promises.access(gitgovPath);
|
|
5935
6758
|
await promises.rm(gitgovPath, { recursive: true, force: true });
|
|
@@ -5993,18 +6816,28 @@ var ProjectAdapter = class {
|
|
|
5993
6816
|
];
|
|
5994
6817
|
await promises.mkdir(gitgovPath, { recursive: true });
|
|
5995
6818
|
for (const dir of directories) {
|
|
5996
|
-
await promises.mkdir(
|
|
6819
|
+
await promises.mkdir(path.join(gitgovPath, dir), { recursive: true });
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
async copyAgentPrompt(gitgovPath) {
|
|
6823
|
+
try {
|
|
6824
|
+
const sourcePrompt = path.join(ConfigManager.findProjectRoot() || process.cwd(), "docs/gitgov_agent_prompt.md");
|
|
6825
|
+
const targetPrompt = path.join(gitgovPath, "gitgov");
|
|
6826
|
+
await promises.copyFile(sourcePrompt, targetPrompt);
|
|
6827
|
+
console.log(`\u{1F4CB} @gitgov agent prompt copied to .gitgov/gitgov`);
|
|
6828
|
+
} catch {
|
|
6829
|
+
console.warn("Warning: Could not copy @gitgov agent prompt. Project will work but AI assistant may not have local instructions.");
|
|
5997
6830
|
}
|
|
5998
6831
|
}
|
|
5999
6832
|
generateProjectId(name) {
|
|
6000
6833
|
return name.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-");
|
|
6001
6834
|
}
|
|
6002
6835
|
async persistConfiguration(config, gitgovPath) {
|
|
6003
|
-
const configPath =
|
|
6836
|
+
const configPath = path.join(gitgovPath, "config.json");
|
|
6004
6837
|
await promises.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
6005
6838
|
}
|
|
6006
6839
|
async initializeSession(actorId, gitgovPath) {
|
|
6007
|
-
const sessionPath =
|
|
6840
|
+
const sessionPath = path.join(gitgovPath, ".session.json");
|
|
6008
6841
|
const session = {
|
|
6009
6842
|
lastSession: {
|
|
6010
6843
|
actorId,
|
|
@@ -6019,7 +6852,7 @@ var ProjectAdapter = class {
|
|
|
6019
6852
|
await promises.writeFile(sessionPath, JSON.stringify(session, null, 2), "utf-8");
|
|
6020
6853
|
}
|
|
6021
6854
|
async setupGitIntegration(projectRoot2) {
|
|
6022
|
-
const gitignorePath =
|
|
6855
|
+
const gitignorePath = path.join(projectRoot2, ".gitignore");
|
|
6023
6856
|
const gitignoreContent = `
|
|
6024
6857
|
# GitGovernance
|
|
6025
6858
|
.gitgov/.session.json
|
|
@@ -6459,7 +7292,6 @@ var workflow_methodology_scrum_default = {
|
|
|
6459
7292
|
required_agents: [
|
|
6460
7293
|
{
|
|
6461
7294
|
id: "agent:scrum-master",
|
|
6462
|
-
gremio: "operations",
|
|
6463
7295
|
engine: {
|
|
6464
7296
|
type: "local",
|
|
6465
7297
|
entrypoint: "@gitgov/agent-scrum-master"
|
|
@@ -6485,7 +7317,6 @@ var workflow_methodology_scrum_default = {
|
|
|
6485
7317
|
},
|
|
6486
7318
|
{
|
|
6487
7319
|
id: "agent:product-owner-assistant",
|
|
6488
|
-
gremio: "strategy",
|
|
6489
7320
|
engine: {
|
|
6490
7321
|
type: "mcp",
|
|
6491
7322
|
url: "http://localhost:8080/product-owner-mcp"
|
|
@@ -6561,11 +7392,19 @@ var WorkflowMethodologyAdapter = class _WorkflowMethodologyAdapter {
|
|
|
6561
7392
|
return this.config;
|
|
6562
7393
|
}
|
|
6563
7394
|
/**
|
|
6564
|
-
*
|
|
7395
|
+
* Determines which signature group to use for validation.
|
|
7396
|
+
* Checks all available signature groups and returns the first one where
|
|
7397
|
+
* the actor has matching capability roles.
|
|
6565
7398
|
*/
|
|
6566
|
-
|
|
6567
|
-
const
|
|
6568
|
-
|
|
7399
|
+
getApplicableSignatureGroup(signatureRules, actor) {
|
|
7400
|
+
for (const [groupName, ruleSet] of Object.entries(signatureRules)) {
|
|
7401
|
+
if (groupName === "__default__") continue;
|
|
7402
|
+
const hasMatchingRole = actor.roles?.some((role) => ruleSet.capability_roles?.includes(role));
|
|
7403
|
+
if (hasMatchingRole) {
|
|
7404
|
+
return groupName;
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
return "__default__";
|
|
6569
7408
|
}
|
|
6570
7409
|
/**
|
|
6571
7410
|
* Determines if a state transition is legal according to the methodology
|
|
@@ -6589,7 +7428,6 @@ var WorkflowMethodologyAdapter = class _WorkflowMethodologyAdapter {
|
|
|
6589
7428
|
*/
|
|
6590
7429
|
async validateSignature(signature, context) {
|
|
6591
7430
|
const config = this.getConfig();
|
|
6592
|
-
const guild = this.getTaskGuild(context);
|
|
6593
7431
|
if (!context.transitionTo) {
|
|
6594
7432
|
throw new Error('ValidationContext must include "transitionTo" for signature validation.');
|
|
6595
7433
|
}
|
|
@@ -6605,7 +7443,8 @@ var WorkflowMethodologyAdapter = class _WorkflowMethodologyAdapter {
|
|
|
6605
7443
|
}
|
|
6606
7444
|
const signatureRules = transitionConfig.requires.signatures;
|
|
6607
7445
|
if (!signatureRules) return true;
|
|
6608
|
-
const
|
|
7446
|
+
const signatureGroup = this.getApplicableSignatureGroup(signatureRules, actor);
|
|
7447
|
+
const ruleSet = signatureRules[signatureGroup];
|
|
6609
7448
|
if (!ruleSet) return false;
|
|
6610
7449
|
if (signature.role !== ruleSet.role) {
|
|
6611
7450
|
return false;
|
|
@@ -6720,9 +7559,11 @@ __export(factories_exports, {
|
|
|
6720
7559
|
createChangelogRecord: () => createChangelogRecord,
|
|
6721
7560
|
createCycleRecord: () => createCycleRecord,
|
|
6722
7561
|
createDefaultWorkflowMethodologyConfig: () => createDefaultWorkflowMethodologyConfig,
|
|
7562
|
+
createEmbeddedMetadataRecord: () => createEmbeddedMetadataRecord,
|
|
6723
7563
|
createExecutionRecord: () => createExecutionRecord,
|
|
6724
7564
|
createFeedbackRecord: () => createFeedbackRecord,
|
|
6725
7565
|
createTaskRecord: () => createTaskRecord,
|
|
7566
|
+
createTestSignature: () => createTestSignature,
|
|
6726
7567
|
createWorkflowMethodologyConfig: () => createWorkflowMethodologyConfig
|
|
6727
7568
|
});
|
|
6728
7569
|
|
|
@@ -6962,6 +7803,68 @@ async function createDefaultWorkflowMethodologyConfig() {
|
|
|
6962
7803
|
});
|
|
6963
7804
|
}
|
|
6964
7805
|
|
|
7806
|
+
// src/factories/embedded_metadata_factory.ts
|
|
7807
|
+
function createTestSignature(keyId = "human:test-user", role = "author", notes = "Test signature - unsigned") {
|
|
7808
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
7809
|
+
return {
|
|
7810
|
+
keyId,
|
|
7811
|
+
role,
|
|
7812
|
+
notes,
|
|
7813
|
+
signature: "dGVzdHNpZ25hdHVyZWJhc2U2NGVuY29kZWRkdW1teWZvcnRlc3RpbmdwdXJwb3Nlc29ubHlub3RyZWFsY3J5cHRvZ3JhcGh5PT0=",
|
|
7814
|
+
// Dummy 88-char base64 for testing (matches Ed25519 signature length)
|
|
7815
|
+
timestamp
|
|
7816
|
+
};
|
|
7817
|
+
}
|
|
7818
|
+
function inferTypeFromPayload(payload) {
|
|
7819
|
+
if ("engine" in payload) return "agent";
|
|
7820
|
+
if ("taskId" in payload && "result" in payload) return "execution";
|
|
7821
|
+
if ("relatedTasks" in payload && "completedAt" in payload) return "changelog";
|
|
7822
|
+
if ("entityType" in payload && "entityId" in payload) return "feedback";
|
|
7823
|
+
if ("status" in payload && "taskIds" in payload) return "cycle";
|
|
7824
|
+
if ("priority" in payload && "description" in payload) return "task";
|
|
7825
|
+
if ("displayName" in payload && "publicKey" in payload) return "actor";
|
|
7826
|
+
return "custom";
|
|
7827
|
+
}
|
|
7828
|
+
async function createEmbeddedMetadataRecord(payload, options = {}) {
|
|
7829
|
+
const inferredType = inferTypeFromPayload(payload);
|
|
7830
|
+
const type = options.header?.type || inferredType;
|
|
7831
|
+
const payloadChecksum = calculatePayloadChecksum(payload);
|
|
7832
|
+
let signatures;
|
|
7833
|
+
if (options.signatures) {
|
|
7834
|
+
signatures = options.signatures;
|
|
7835
|
+
} else if (options.signature?.privateKey) {
|
|
7836
|
+
const keyId = options.signature.keyId || "human:test-user";
|
|
7837
|
+
const role = options.signature.role || "author";
|
|
7838
|
+
const notes = options.signature.notes || "Created via factory";
|
|
7839
|
+
signatures = [signPayload(payload, options.signature.privateKey, keyId, role, notes)];
|
|
7840
|
+
} else {
|
|
7841
|
+
const keyId = options.signature?.keyId || "human:test-user";
|
|
7842
|
+
const role = options.signature?.role || "author";
|
|
7843
|
+
const notes = options.signature?.notes || "Test signature - unsigned";
|
|
7844
|
+
signatures = [createTestSignature(keyId, role, notes)];
|
|
7845
|
+
}
|
|
7846
|
+
const header = {
|
|
7847
|
+
version: "1.0",
|
|
7848
|
+
// Always 1.0 (schema enforces this)
|
|
7849
|
+
type,
|
|
7850
|
+
payloadChecksum,
|
|
7851
|
+
signatures,
|
|
7852
|
+
...type === "custom" && {
|
|
7853
|
+
schemaUrl: options.header?.schemaUrl,
|
|
7854
|
+
schemaChecksum: options.header?.schemaChecksum
|
|
7855
|
+
}
|
|
7856
|
+
};
|
|
7857
|
+
const embeddedRecord = {
|
|
7858
|
+
header,
|
|
7859
|
+
payload
|
|
7860
|
+
};
|
|
7861
|
+
const validation = validateEmbeddedMetadataDetailed(embeddedRecord);
|
|
7862
|
+
if (!validation.isValid) {
|
|
7863
|
+
throw new DetailedValidationError("EmbeddedMetadataRecord", validation.errors);
|
|
7864
|
+
}
|
|
7865
|
+
return embeddedRecord;
|
|
7866
|
+
}
|
|
7867
|
+
|
|
6965
7868
|
// src/validation/index.ts
|
|
6966
7869
|
var validation_exports = {};
|
|
6967
7870
|
__export(validation_exports, {
|
|
@@ -7025,9 +7928,11 @@ function generateSubscriptionId() {
|
|
|
7025
7928
|
var EventBus = class {
|
|
7026
7929
|
emitter;
|
|
7027
7930
|
subscriptions;
|
|
7931
|
+
pendingHandlers;
|
|
7028
7932
|
constructor() {
|
|
7029
7933
|
this.emitter = new EventEmitter();
|
|
7030
7934
|
this.subscriptions = /* @__PURE__ */ new Map();
|
|
7935
|
+
this.pendingHandlers = /* @__PURE__ */ new Set();
|
|
7031
7936
|
this.emitter.setMaxListeners(100);
|
|
7032
7937
|
}
|
|
7033
7938
|
/**
|
|
@@ -7058,11 +7963,17 @@ var EventBus = class {
|
|
|
7058
7963
|
subscribe(eventType, handler) {
|
|
7059
7964
|
const subscriptionId = generateSubscriptionId();
|
|
7060
7965
|
const wrappedHandler = async (event) => {
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7966
|
+
const handlerPromise = (async () => {
|
|
7967
|
+
try {
|
|
7968
|
+
await handler(event);
|
|
7969
|
+
} catch (error) {
|
|
7970
|
+
console.error(`Error in event handler for ${eventType}:`, error);
|
|
7971
|
+
}
|
|
7972
|
+
})();
|
|
7973
|
+
this.pendingHandlers.add(handlerPromise);
|
|
7974
|
+
handlerPromise.finally(() => {
|
|
7975
|
+
this.pendingHandlers.delete(handlerPromise);
|
|
7976
|
+
});
|
|
7066
7977
|
};
|
|
7067
7978
|
const subscription = {
|
|
7068
7979
|
id: subscriptionId,
|
|
@@ -7134,6 +8045,43 @@ var EventBus = class {
|
|
|
7134
8045
|
subscribeToAll(handler) {
|
|
7135
8046
|
return this.subscribe("*", handler);
|
|
7136
8047
|
}
|
|
8048
|
+
/**
|
|
8049
|
+
* Wait for all pending event handlers to complete.
|
|
8050
|
+
* This is primarily useful for testing to ensure event handlers finish before assertions.
|
|
8051
|
+
*
|
|
8052
|
+
* In production, events are fire-and-forget for performance.
|
|
8053
|
+
* In tests, use this to synchronize and avoid race conditions.
|
|
8054
|
+
*
|
|
8055
|
+
* @param options - Optional configuration
|
|
8056
|
+
* @param options.timeout - Maximum time to wait in ms (default: 5000)
|
|
8057
|
+
* @returns Promise that resolves when all handlers complete or timeout occurs
|
|
8058
|
+
*
|
|
8059
|
+
* @example
|
|
8060
|
+
* ```typescript
|
|
8061
|
+
* await feedbackAdapter.create(...); // publishes event
|
|
8062
|
+
* await eventBus.waitForIdle(); // wait for BacklogAdapter.handleFeedbackCreated()
|
|
8063
|
+
* const task = await backlogAdapter.getTask(taskId);
|
|
8064
|
+
* expect(task.status).toBe('paused'); // now safe to assert
|
|
8065
|
+
* ```
|
|
8066
|
+
*/
|
|
8067
|
+
async waitForIdle(options = {}) {
|
|
8068
|
+
const timeout = options.timeout ?? 5e3;
|
|
8069
|
+
const startTime = Date.now();
|
|
8070
|
+
while (this.pendingHandlers.size > 0) {
|
|
8071
|
+
if (Date.now() - startTime > timeout) {
|
|
8072
|
+
const pendingCount = this.pendingHandlers.size;
|
|
8073
|
+
console.warn(`EventBus.waitForIdle() timeout after ${timeout}ms with ${pendingCount} handlers still pending`);
|
|
8074
|
+
break;
|
|
8075
|
+
}
|
|
8076
|
+
if (this.pendingHandlers.size > 0) {
|
|
8077
|
+
await Promise.race([
|
|
8078
|
+
Promise.all(Array.from(this.pendingHandlers)),
|
|
8079
|
+
new Promise((resolve) => setTimeout(resolve, 10))
|
|
8080
|
+
// Re-check every 10ms
|
|
8081
|
+
]);
|
|
8082
|
+
}
|
|
8083
|
+
}
|
|
8084
|
+
}
|
|
7137
8085
|
};
|
|
7138
8086
|
var eventBus = new EventBus();
|
|
7139
8087
|
function publishEvent(event) {
|
|
@@ -7327,14 +8275,17 @@ var RelationshipAnalyzer = class {
|
|
|
7327
8275
|
for (const task of tasks) {
|
|
7328
8276
|
const isEpic = this.isEpicTask(task);
|
|
7329
8277
|
const title = task.title || "Untitled Task";
|
|
7330
|
-
|
|
8278
|
+
const node = {
|
|
7331
8279
|
id: this.generateNodeId(task),
|
|
7332
8280
|
type: isEpic ? "epic-task" : "task",
|
|
7333
8281
|
title,
|
|
7334
8282
|
status: task.status,
|
|
7335
|
-
tags: task.tags,
|
|
7336
8283
|
originalId: task.id
|
|
7337
|
-
}
|
|
8284
|
+
};
|
|
8285
|
+
if (task.tags) {
|
|
8286
|
+
node.tags = task.tags;
|
|
8287
|
+
}
|
|
8288
|
+
nodes.push(node);
|
|
7338
8289
|
}
|
|
7339
8290
|
return nodes;
|
|
7340
8291
|
}
|
|
@@ -8014,14 +8965,14 @@ var DiagramGenerator = class {
|
|
|
8014
8965
|
* Loads all cycle records from the filesystem
|
|
8015
8966
|
*/
|
|
8016
8967
|
async loadCycleRecords(gitgovPath) {
|
|
8017
|
-
const cyclesDir =
|
|
8968
|
+
const cyclesDir = path.join(gitgovPath, "cycles");
|
|
8018
8969
|
try {
|
|
8019
8970
|
const files = await promises.readdir(cyclesDir);
|
|
8020
8971
|
const jsonFiles = files.filter((file) => file.endsWith(".json"));
|
|
8021
8972
|
const cycles = [];
|
|
8022
8973
|
for (const file of jsonFiles) {
|
|
8023
8974
|
try {
|
|
8024
|
-
const filePath =
|
|
8975
|
+
const filePath = path.join(cyclesDir, file);
|
|
8025
8976
|
const content = await promises.readFile(filePath, "utf-8");
|
|
8026
8977
|
const record = JSON.parse(content);
|
|
8027
8978
|
if (record.payload && record.payload.id) {
|
|
@@ -8050,14 +9001,14 @@ var DiagramGenerator = class {
|
|
|
8050
9001
|
* Loads all task records from the filesystem
|
|
8051
9002
|
*/
|
|
8052
9003
|
async loadTaskRecords(gitgovPath) {
|
|
8053
|
-
const tasksDir =
|
|
9004
|
+
const tasksDir = path.join(gitgovPath, "tasks");
|
|
8054
9005
|
try {
|
|
8055
9006
|
const files = await promises.readdir(tasksDir);
|
|
8056
9007
|
const jsonFiles = files.filter((file) => file.endsWith(".json"));
|
|
8057
9008
|
const tasks = [];
|
|
8058
9009
|
for (const file of jsonFiles) {
|
|
8059
9010
|
try {
|
|
8060
|
-
const filePath =
|
|
9011
|
+
const filePath = path.join(tasksDir, file);
|
|
8061
9012
|
const content = await promises.readFile(filePath, "utf-8");
|
|
8062
9013
|
const record = JSON.parse(content);
|
|
8063
9014
|
if (record.payload && record.payload.id) {
|