@gitgov/core 1.8.0 → 1.9.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 CHANGED
@@ -4,7 +4,7 @@
4
4
  [![License: MPL-2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](./tsconfig.json)
6
6
 
7
- `@gitgov/core` is the **SDK** for the GitGovernance ecosystem. It provides a type-safe, local-first, and schema-driven API to manage identities, agents, and workflows in software projects.
7
+ `@gitgov/core` is the **SDK** for the GitGovernance ecosystem. It provides a type-safe, local-first, and schema-driven API to manage identities, agents, tasks, and workflows in software projects.
8
8
 
9
9
  ## 🚀 Quick Start
10
10
 
@@ -54,7 +54,7 @@ interface ActorRecord {
54
54
  /**
55
55
  * Canonical schema for agent operational manifests.
56
56
  */
57
- interface AgentRecord {
57
+ interface AgentRecord<TMetadata = object> {
58
58
  /**
59
59
  * Unique identifier for the agent, linking to an ActorRecord.
60
60
  */
@@ -87,9 +87,7 @@ interface AgentRecord {
87
87
  * This field does NOT affect agent execution - it is purely informational.
88
88
  *
89
89
  */
90
- metadata?: {
91
- [k: string]: unknown | undefined;
92
- };
90
+ metadata?: TMetadata;
93
91
  engine: {
94
92
  type: 'local';
95
93
  /**
@@ -274,7 +272,7 @@ interface CycleRecord {
274
272
  /**
275
273
  * Canonical schema for execution log records - the universal event stream
276
274
  */
277
- interface ExecutionRecord {
275
+ interface ExecutionRecord<TMetadata = object> {
278
276
  /**
279
277
  * Unique identifier for the execution log entry (10 timestamp + 1 dash + 4 'exec' + 1 dash + max 50 slug = 66 max)
280
278
  */
@@ -317,6 +315,15 @@ interface ExecutionRecord {
317
315
  *
318
316
  */
319
317
  references?: string[];
318
+ /**
319
+ * Optional structured data for machine consumption.
320
+ * Use this field for data that needs to be programmatically processed (e.g., audit findings,
321
+ * performance metrics, scan results). This complements result (human-readable WHAT) and
322
+ * notes (narrative HOW/WHY) by providing structured, queryable data.
323
+ * Common use cases: audit findings arrays, performance metrics, tool outputs, scan summaries.
324
+ *
325
+ */
326
+ metadata?: TMetadata;
320
327
  }
321
328
 
322
329
  /**
@@ -754,7 +761,7 @@ declare class GitGovError extends Error {
754
761
  type index$m_ActorPayload = ActorPayload;
755
762
  type index$m_ActorRecord = ActorRecord;
756
763
  type index$m_AgentPayload = AgentPayload;
757
- type index$m_AgentRecord = AgentRecord;
764
+ type index$m_AgentRecord<TMetadata = object> = AgentRecord<TMetadata>;
758
765
  type index$m_ChangelogPayload = ChangelogPayload;
759
766
  type index$m_ChangelogRecord = ChangelogRecord;
760
767
  type index$m_CustomRecord = CustomRecord;
@@ -763,7 +770,7 @@ type index$m_CycleRecord = CycleRecord;
763
770
  type index$m_EmbeddedMetadataHeader = EmbeddedMetadataHeader;
764
771
  type index$m_EmbeddedMetadataRecord<T extends GitGovRecordPayload> = EmbeddedMetadataRecord<T>;
765
772
  type index$m_ExecutionPayload = ExecutionPayload;
766
- type index$m_ExecutionRecord = ExecutionRecord;
773
+ type index$m_ExecutionRecord<TMetadata = object> = ExecutionRecord<TMetadata>;
767
774
  type index$m_FeedbackPayload = FeedbackPayload;
768
775
  type index$m_FeedbackRecord = FeedbackRecord;
769
776
  type index$m_GitGovActorRecord = GitGovActorRecord;
@@ -1218,7 +1225,7 @@ interface IIdentityAdapter {
1218
1225
  resolveCurrentActorId(originalActorId: string): Promise<string>;
1219
1226
  getCurrentActor(): Promise<ActorRecord>;
1220
1227
  getEffectiveActorForAgent(agentId: string): Promise<ActorRecord | null>;
1221
- signRecord(record: GitGovRecord, actorId: string, role: string): Promise<GitGovRecord>;
1228
+ signRecord(record: GitGovRecord, actorId: string, role: string, notes: string): Promise<GitGovRecord>;
1222
1229
  rotateActorKey(actorId: string): Promise<{
1223
1230
  oldActor: ActorRecord;
1224
1231
  newActor: ActorRecord;
@@ -1249,7 +1256,7 @@ declare class IdentityAdapter implements IIdentityAdapter {
1249
1256
  createActor(payload: ActorPayload, _signerId: string): Promise<ActorRecord>;
1250
1257
  getActor(actorId: string): Promise<ActorRecord | null>;
1251
1258
  listActors(): Promise<ActorRecord[]>;
1252
- signRecord(record: GitGovRecord, actorId: string, role: string): Promise<GitGovRecord>;
1259
+ signRecord(record: GitGovRecord, actorId: string, role: string, notes: string): Promise<GitGovRecord>;
1253
1260
  /**
1254
1261
  * Resolves the current active ActorRecord ID by following the succession chain.
1255
1262
  * This is critical for AgentRecord operations after key rotation.
@@ -1887,8 +1894,15 @@ declare class ConfigManager {
1887
1894
  loadConfig(): Promise<GitGovConfig | null>;
1888
1895
  /**
1889
1896
  * Load GitGovernance session state
1897
+ * [EARS-53] Auto-detects actor from .key files if no session or no actorId exists
1890
1898
  */
1891
1899
  loadSession(): Promise<GitGovSession | null>;
1900
+ /**
1901
+ * [EARS-53] Detect actor from .key files in .gitgov/actors/
1902
+ * Returns the actor ID if exactly one .key file exists, or the first one if multiple exist.
1903
+ * Private keys (.key files) indicate which actors can sign on this machine.
1904
+ */
1905
+ detectActorFromKeyFiles(): Promise<string | null>;
1892
1906
  /**
1893
1907
  * Get root cycle from configuration
1894
1908
  */
@@ -2139,7 +2153,7 @@ interface IBacklogAdapter {
2139
2153
  getAllTasks(): Promise<TaskRecord[]>;
2140
2154
  submitTask(taskId: string, actorId: string): Promise<TaskRecord>;
2141
2155
  approveTask(taskId: string, actorId: string): Promise<TaskRecord>;
2142
- updateTask(taskId: string, payload: Partial<TaskRecord>): Promise<TaskRecord>;
2156
+ updateTask(taskId: string, payload: Partial<TaskRecord>, actorId: string): Promise<TaskRecord>;
2143
2157
  activateTask(taskId: string, actorId: string): Promise<TaskRecord>;
2144
2158
  completeTask(taskId: string, actorId: string): Promise<TaskRecord>;
2145
2159
  pauseTask(taskId: string, actorId: string, reason?: string): Promise<TaskRecord>;
@@ -2252,8 +2266,9 @@ declare class BacklogAdapter implements IBacklogAdapter {
2252
2266
  deleteTask(taskId: string, actorId: string): Promise<void>;
2253
2267
  /**
2254
2268
  * Updates a task with new payload
2269
+ * [EARS-28] Signs the updated record with the editor's signature
2255
2270
  */
2256
- updateTask(taskId: string, payload: Partial<TaskRecord>): Promise<TaskRecord>;
2271
+ updateTask(taskId: string, payload: Partial<TaskRecord>, actorId: string): Promise<TaskRecord>;
2257
2272
  /**
2258
2273
  * Gets tasks assigned to a specific actor
2259
2274
  */
@@ -3748,12 +3763,23 @@ declare function createCycleRecord(payload: Partial<CycleRecord>): CycleRecord;
3748
3763
  declare function loadCycleRecord(data: unknown): GitGovCycleRecord;
3749
3764
 
3750
3765
  /**
3751
- * Creates a complete ExecutionRecord with validation
3766
+ * Creates a complete ExecutionRecord with validation.
3767
+ *
3768
+ * The factory is generic to preserve the metadata type for compile-time safety.
3769
+ *
3770
+ * @param payload - Partial ExecutionRecord payload with optional typed metadata
3771
+ * @returns ExecutionRecord<TMetadata> - The validated ExecutionRecord with preserved metadata type
3752
3772
  *
3753
- * @param payload - Partial ExecutionRecord payload
3754
- * @returns ExecutionRecord - The validated ExecutionRecord
3773
+ * @example
3774
+ * interface AuditMetadata { scannedFiles: number; }
3775
+ * const record = createExecutionRecord<AuditMetadata>({
3776
+ * taskId: '1752274500-task-audit',
3777
+ * result: 'Audit complete',
3778
+ * metadata: { scannedFiles: 245 }
3779
+ * });
3780
+ * // record.metadata?.scannedFiles is typed as number
3755
3781
  */
3756
- declare function createExecutionRecord(payload: Partial<ExecutionRecord>): ExecutionRecord;
3782
+ declare function createExecutionRecord<TMetadata extends object = object>(payload: Partial<ExecutionRecord<TMetadata>>): ExecutionRecord<TMetadata>;
3757
3783
  /**
3758
3784
  * Loads and validates an existing ExecutionRecord from untrusted data.
3759
3785
  * Used by RecordStore to validate records when reading from disk.
@@ -5667,8 +5693,29 @@ declare const Schemas: {
5667
5693
  default: never[];
5668
5694
  description: string;
5669
5695
  };
5696
+ metadata: {
5697
+ type: string;
5698
+ additionalProperties: boolean;
5699
+ description: string;
5700
+ examples: ({
5701
+ findings: {
5702
+ type: string;
5703
+ file: string;
5704
+ line: number;
5705
+ }[];
5706
+ scannedFiles: number;
5707
+ metrics?: never;
5708
+ } | {
5709
+ metrics: {
5710
+ duration_ms: number;
5711
+ memory_mb: number;
5712
+ };
5713
+ findings?: never;
5714
+ scannedFiles?: never;
5715
+ })[];
5716
+ };
5670
5717
  };
5671
- examples: {
5718
+ examples: ({
5672
5719
  id: string;
5673
5720
  taskId: string;
5674
5721
  type: string;
@@ -5676,7 +5723,34 @@ declare const Schemas: {
5676
5723
  result: string;
5677
5724
  notes: string;
5678
5725
  references: string[];
5679
- }[];
5726
+ metadata?: never;
5727
+ } | {
5728
+ id: string;
5729
+ taskId: string;
5730
+ type: string;
5731
+ title: string;
5732
+ result: string;
5733
+ notes: string;
5734
+ references: string[];
5735
+ metadata: {
5736
+ scannedFiles: number;
5737
+ scannedLines: number;
5738
+ duration_ms: number;
5739
+ findings: {
5740
+ id: string;
5741
+ severity: string;
5742
+ file: string;
5743
+ line: number;
5744
+ type: string;
5745
+ }[];
5746
+ summary: {
5747
+ critical: number;
5748
+ high: number;
5749
+ medium: number;
5750
+ low: number;
5751
+ };
5752
+ };
5753
+ })[];
5680
5754
  };
5681
5755
  readonly FeedbackRecord: {
5682
5756
  $schema: string;
@@ -7451,8 +7525,29 @@ declare function getSchema(name: SchemaName): {
7451
7525
  default: never[];
7452
7526
  description: string;
7453
7527
  };
7528
+ metadata: {
7529
+ type: string;
7530
+ additionalProperties: boolean;
7531
+ description: string;
7532
+ examples: ({
7533
+ findings: {
7534
+ type: string;
7535
+ file: string;
7536
+ line: number;
7537
+ }[];
7538
+ scannedFiles: number;
7539
+ metrics?: never;
7540
+ } | {
7541
+ metrics: {
7542
+ duration_ms: number;
7543
+ memory_mb: number;
7544
+ };
7545
+ findings?: never;
7546
+ scannedFiles?: never;
7547
+ })[];
7548
+ };
7454
7549
  };
7455
- examples: {
7550
+ examples: ({
7456
7551
  id: string;
7457
7552
  taskId: string;
7458
7553
  type: string;
@@ -7460,7 +7555,34 @@ declare function getSchema(name: SchemaName): {
7460
7555
  result: string;
7461
7556
  notes: string;
7462
7557
  references: string[];
7463
- }[];
7558
+ metadata?: never;
7559
+ } | {
7560
+ id: string;
7561
+ taskId: string;
7562
+ type: string;
7563
+ title: string;
7564
+ result: string;
7565
+ notes: string;
7566
+ references: string[];
7567
+ metadata: {
7568
+ scannedFiles: number;
7569
+ scannedLines: number;
7570
+ duration_ms: number;
7571
+ findings: {
7572
+ id: string;
7573
+ severity: string;
7574
+ file: string;
7575
+ line: number;
7576
+ type: string;
7577
+ }[];
7578
+ summary: {
7579
+ critical: number;
7580
+ high: number;
7581
+ medium: number;
7582
+ low: number;
7583
+ };
7584
+ };
7585
+ })[];
7464
7586
  } | {
7465
7587
  $schema: string;
7466
7588
  $id: string;
@@ -8296,6 +8418,15 @@ interface SyncPushResult {
8296
8418
  conflictInfo?: ConflictInfo;
8297
8419
  /** Error message if operation failed */
8298
8420
  error?: string;
8421
+ /** [EARS-54] Implicit pull results when push does reconciliation with remote */
8422
+ implicitPull?: {
8423
+ /** Whether changes were pulled from remote */
8424
+ hasChanges: boolean;
8425
+ /** Number of files updated from remote */
8426
+ filesUpdated: number;
8427
+ /** Whether index was regenerated */
8428
+ reindexed: boolean;
8429
+ };
8299
8430
  }
8300
8431
  /**
8301
8432
  * Options for pullState operation
@@ -8303,6 +8434,8 @@ interface SyncPushResult {
8303
8434
  interface SyncPullOptions {
8304
8435
  /** Force re-indexing even if there are no new changes */
8305
8436
  forceReindex?: boolean;
8437
+ /** [EARS-62] Force pull even if local changes would be overwritten */
8438
+ force?: boolean;
8306
8439
  }
8307
8440
  /**
8308
8441
  * Result of pullState operation
@@ -8322,6 +8455,8 @@ interface SyncPullResult {
8322
8455
  conflictInfo?: ConflictInfo;
8323
8456
  /** Error message if operation failed */
8324
8457
  error?: string;
8458
+ /** [EARS-62] Files that were forcefully overwritten (when force: true) */
8459
+ forcedOverwrites?: string[];
8325
8460
  }
8326
8461
  /**
8327
8462
  * Options for resolveConflict operation
@@ -8366,8 +8501,13 @@ interface ConflictInfo {
8366
8501
  }
8367
8502
  /**
8368
8503
  * Auxiliary type to identify the conflict type
8504
+ *
8505
+ * Git-Native conflict model (post-refactor):
8506
+ * - rebase_conflict: Used for all Git-level conflicts during push/pull
8507
+ * - local_changes_conflict: Used when pull would overwrite local changes (EARS-61)
8508
+ * - integrity_violation: Used when resolution commits are missing
8369
8509
  */
8370
- type ConflictType = "rebase_conflict" | "merge_conflict" | "integrity_violation" | "unresolved_markers";
8510
+ type ConflictType = "rebase_conflict" | "integrity_violation" | "local_changes_conflict";
8371
8511
  /**
8372
8512
  * Information about a detected integrity violation
8373
8513
  */
@@ -8541,6 +8681,17 @@ declare class SyncModule {
8541
8681
  * [EARS-5]
8542
8682
  */
8543
8683
  calculateStateDelta(sourceBranch: string): Promise<StateDeltaFile[]>;
8684
+ /**
8685
+ * [EARS-60] Detect file-level conflicts and identify remote-only changes.
8686
+ *
8687
+ * A conflict exists when:
8688
+ * 1. A file was modified by the remote during implicit pull
8689
+ * 2. AND the LOCAL USER also modified that same file (content in tempDir differs from what was in git before pull)
8690
+ *
8691
+ * This catches conflicts that git rebase can't detect because we copy files AFTER the pull.
8692
+ *
8693
+ * @param tempDir - Directory containing local .gitgov/ files (preserved before checkout)
8694
+ * @param repoRoot - Repository root path
8544
8695
  /**
8545
8696
  * Checks if a rebase is in progress.
8546
8697
  *
@@ -8591,9 +8742,20 @@ declare class SyncModule {
8591
8742
  */
8592
8743
  pullState(options?: SyncPullOptions): Promise<SyncPullResult>;
8593
8744
  /**
8594
- * Resolves state conflicts in a governed manner.
8595
- * Updates resolved Records (recalculates checksum and adds resolver signature),
8596
- * creates rebase and resolution commits signed according to protocol.
8745
+ * Resolves state conflicts in a governed manner (Git-Native).
8746
+ *
8747
+ * Git-Native Flow:
8748
+ * 1. User resolves conflicts using standard Git tools (edit files, remove markers)
8749
+ * 2. User stages resolved files: git add .gitgov/
8750
+ * 3. User runs: gitgov sync resolve --reason "reason"
8751
+ *
8752
+ * This method:
8753
+ * - Verifies that a rebase is in progress
8754
+ * - Checks that no conflict markers remain in staged files
8755
+ * - Updates resolved Records with new checksums and signatures
8756
+ * - Continues the git rebase (git rebase --continue)
8757
+ * - Creates a signed resolution commit
8758
+ * - Regenerates the index
8597
8759
  *
8598
8760
  * [EARS-17 through EARS-23]
8599
8761
  */