@gitgov/core 2.6.0 → 2.7.1

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/dist/src/index.js CHANGED
@@ -985,20 +985,10 @@ var embedded_metadata_schema_default = {
985
985
  "execution",
986
986
  "changelog",
987
987
  "feedback",
988
- "cycle",
989
- "custom"
988
+ "cycle"
990
989
  ],
991
990
  description: "The type of the record contained in the payload."
992
991
  },
993
- schemaUrl: {
994
- type: "string",
995
- description: "Optional URL to a custom schema for the payload."
996
- },
997
- schemaChecksum: {
998
- type: "string",
999
- pattern: "^[a-fA-F0-9]{64}$",
1000
- description: "Optional SHA-256 checksum of the custom schema."
1001
- },
1002
992
  payloadChecksum: {
1003
993
  type: "string",
1004
994
  pattern: "^[a-fA-F0-9]{64}$",
@@ -1222,32 +1212,6 @@ var embedded_metadata_schema_default = {
1222
1212
  }
1223
1213
  },
1224
1214
  else: false
1225
- },
1226
- {
1227
- if: {
1228
- properties: {
1229
- header: {
1230
- type: "object",
1231
- properties: {
1232
- type: {
1233
- const: "custom"
1234
- }
1235
- }
1236
- }
1237
- }
1238
- },
1239
- then: {
1240
- properties: {
1241
- header: {
1242
- type: "object",
1243
- required: [
1244
- "schemaUrl",
1245
- "schemaChecksum"
1246
- ]
1247
- }
1248
- }
1249
- },
1250
- else: false
1251
1215
  }
1252
1216
  ],
1253
1217
  examples: [
@@ -6915,7 +6879,7 @@ function inferTypeFromPayload(payload) {
6915
6879
  if ("status" in payload && "taskIds" in payload) return "cycle";
6916
6880
  if ("priority" in payload && "description" in payload) return "task";
6917
6881
  if ("displayName" in payload && "publicKey" in payload) return "actor";
6918
- return "custom";
6882
+ return "task";
6919
6883
  }
6920
6884
  function createEmbeddedMetadataRecord(payload, options = {}) {
6921
6885
  const inferredType = inferTypeFromPayload(payload);
@@ -6940,11 +6904,7 @@ function createEmbeddedMetadataRecord(payload, options = {}) {
6940
6904
  // Always 1.0 (schema enforces this)
6941
6905
  type,
6942
6906
  payloadChecksum,
6943
- signatures,
6944
- ...type === "custom" && {
6945
- schemaUrl: options.header?.schemaUrl,
6946
- schemaChecksum: options.header?.schemaChecksum
6947
- }
6907
+ signatures
6948
6908
  };
6949
6909
  const embeddedRecord = {
6950
6910
  header,
@@ -7693,6 +7653,22 @@ var LintModule = class {
7693
7653
  }
7694
7654
  return results;
7695
7655
  }
7656
+ /**
7657
+ * Validates typed references by prefix (pure, no I/O).
7658
+ * Delegates to private validateTypedReferencesByPrefix.
7659
+ */
7660
+ lintRecordReferences(record, context) {
7661
+ const payload = record.payload;
7662
+ if (!Array.isArray(payload.references) || payload.references.length === 0) {
7663
+ return [];
7664
+ }
7665
+ return this.validateTypedReferencesByPrefix(
7666
+ payload.references,
7667
+ context.recordId,
7668
+ context.filePath || `unknown/${context.recordId}.json`,
7669
+ context.entityType
7670
+ );
7671
+ }
7696
7672
  /**
7697
7673
  * Validates all records from stores.
7698
7674
  * Iterates this.stores to collect records, then validates each one.
@@ -7810,7 +7786,7 @@ var LintModule = class {
7810
7786
  fixedRecord = this.fixChecksum(fixedRecord);
7811
7787
  break;
7812
7788
  case "SIGNATURE_STRUCTURE":
7813
- fixedRecord = this.fixSignature(fixedRecord, result, options);
7789
+ fixedRecord = this.fixSignature(fixedRecord, result, options, fixableResults);
7814
7790
  break;
7815
7791
  // BIDIRECTIONAL_CONSISTENCY requires modifying OTHER records
7816
7792
  // This must be handled by FsLintModule which can write multiple files
@@ -7909,6 +7885,45 @@ var LintModule = class {
7909
7885
  }
7910
7886
  return results;
7911
7887
  }
7888
+ /**
7889
+ * [EARS-E2] Validates typed references by prefix.
7890
+ * Valid prefixes: file:, task:, cycle:, adapter:, url:, commit:, pr:
7891
+ * Reports a warning for unknown prefixes and an error for empty values after known prefix.
7892
+ * Pure — does not resolve actual references.
7893
+ * @private
7894
+ */
7895
+ validateTypedReferencesByPrefix(references, recordId, filePath, entityType) {
7896
+ const VALID_PREFIXES = ["file:", "task:", "cycle:", "adapter:", "url:", "commit:", "pr:"];
7897
+ const results = [];
7898
+ for (const ref of references) {
7899
+ const matchedPrefix = VALID_PREFIXES.find((p) => ref.startsWith(p));
7900
+ if (!matchedPrefix) {
7901
+ results.push({
7902
+ level: "warning",
7903
+ filePath,
7904
+ validator: "REFERENTIAL_INTEGRITY",
7905
+ message: `Reference '${ref}' has an unknown prefix. Valid prefixes: ${VALID_PREFIXES.join(", ")}`,
7906
+ entity: { type: entityType, id: recordId },
7907
+ fixable: false,
7908
+ context: { field: "references", actual: ref, expected: "reference with valid prefix" }
7909
+ });
7910
+ } else {
7911
+ const value = ref.slice(matchedPrefix.length);
7912
+ if (!value) {
7913
+ results.push({
7914
+ level: "error",
7915
+ filePath,
7916
+ validator: "REFERENTIAL_INTEGRITY",
7917
+ message: `Reference '${ref}' has prefix '${matchedPrefix}' but no value after it`,
7918
+ entity: { type: entityType, id: recordId },
7919
+ fixable: false,
7920
+ context: { field: "references", actual: ref, expected: `${matchedPrefix}<non-empty-value>` }
7921
+ });
7922
+ }
7923
+ }
7924
+ }
7925
+ return results;
7926
+ }
7912
7927
  /**
7913
7928
  * [EARS-E1] through [EARS-E6] Validates references.
7914
7929
  * Uses Store<T> for lookups.
@@ -7917,6 +7932,16 @@ var LintModule = class {
7917
7932
  async validateReferences(record, recordId, filePath, entityType) {
7918
7933
  const results = [];
7919
7934
  const payload = record.payload;
7935
+ const payloadWithRefs = payload;
7936
+ if (Array.isArray(payloadWithRefs.references) && payloadWithRefs.references.length > 0) {
7937
+ const prefixResults = this.validateTypedReferencesByPrefix(
7938
+ payloadWithRefs.references,
7939
+ recordId,
7940
+ filePath,
7941
+ entityType
7942
+ );
7943
+ results.push(...prefixResults);
7944
+ }
7920
7945
  const taskStore = this.stores.tasks;
7921
7946
  const cycleStore = this.stores.cycles;
7922
7947
  if (entityType === "execution" && isExecutionPayload(payload) && payload.taskId && taskStore) {
@@ -8119,23 +8144,59 @@ var LintModule = class {
8119
8144
  };
8120
8145
  }
8121
8146
  /**
8122
- * Fixes signature structure issues.
8123
- * @private
8124
- */
8125
- /**
8126
- * [EARS-F9] Fixes signature structure issues.
8147
+ * [EARS-F9] [EARS-F10] Fixes signature structure issues.
8127
8148
  *
8128
8149
  * Preserves valid keyId and role from existing signature if present,
8129
8150
  * only using options.keyId as fallback when no existing signature exists.
8130
8151
  *
8152
+ * [EARS-F10] When the ONLY problem is a missing `notes` field (no other
8153
+ * signature errors exist for this record), adds `notes` WITHOUT regenerating
8154
+ * the signature — preserving the existing cryptographic value.
8155
+ *
8131
8156
  * @private
8132
8157
  */
8133
- fixSignature(record, result, options) {
8158
+ fixSignature(record, result, options, allResults) {
8134
8159
  if (!options.privateKey) {
8135
8160
  throw new Error("privateKey is required to fix signature");
8136
8161
  }
8137
- const payloadChecksum = calculatePayloadChecksum(record.payload);
8138
8162
  const existingSig = record.header?.signatures?.[0];
8163
+ const isOnlyMissingNotes = (() => {
8164
+ if (!result.message.includes("must have required property 'notes'")) {
8165
+ return false;
8166
+ }
8167
+ if (allResults) {
8168
+ const otherSigErrors = allResults.filter(
8169
+ (r) => r !== result && r.validator === "SIGNATURE_STRUCTURE" && r.entity.id === result.entity.id && r.fixable
8170
+ );
8171
+ if (otherSigErrors.length > 0) {
8172
+ return false;
8173
+ }
8174
+ }
8175
+ const sigValue = existingSig?.signature;
8176
+ if (!sigValue || !/^[A-Za-z0-9+/]{86}==$/.test(sigValue)) {
8177
+ return false;
8178
+ }
8179
+ return true;
8180
+ })();
8181
+ if (isOnlyMissingNotes && existingSig) {
8182
+ const fixedSig = {
8183
+ keyId: existingSig.keyId,
8184
+ role: existingSig.role,
8185
+ notes: "Signature notes added by lint fix",
8186
+ signature: existingSig.signature,
8187
+ timestamp: existingSig.timestamp
8188
+ };
8189
+ const payloadChecksum2 = calculatePayloadChecksum(record.payload);
8190
+ return {
8191
+ header: {
8192
+ ...record.header,
8193
+ payloadChecksum: payloadChecksum2,
8194
+ signatures: [fixedSig]
8195
+ },
8196
+ payload: record.payload
8197
+ };
8198
+ }
8199
+ const payloadChecksum = calculatePayloadChecksum(record.payload);
8139
8200
  const keyId = existingSig?.keyId || options.keyId || result.entity.id;
8140
8201
  const role = existingSig?.role || "author";
8141
8202
  const notes = existingSig?.notes || "Signature regenerated by lint fix";
@@ -8229,6 +8290,7 @@ __export(sync_state_exports, {
8229
8290
  ActorIdentityMismatchError: () => ActorIdentityMismatchError,
8230
8291
  ConflictMarkersPresentError: () => ConflictMarkersPresentError,
8231
8292
  CryptoModuleRequiredError: () => CryptoModuleRequiredError,
8293
+ DEFAULT_STATE_BRANCH: () => DEFAULT_STATE_BRANCH,
8232
8294
  IntegrityViolationError: () => IntegrityViolationError,
8233
8295
  NoRebaseInProgressError: () => NoRebaseInProgressError,
8234
8296
  PullScheduler: () => PullScheduler,
@@ -8440,6 +8502,9 @@ var PullScheduler = class {
8440
8502
  }
8441
8503
  };
8442
8504
 
8505
+ // src/sync_state/fs_worktree/fs_worktree_sync_state.types.ts
8506
+ var DEFAULT_STATE_BRANCH = "gitgov-state";
8507
+
8443
8508
  // src/sync_state/sync_state.errors.ts
8444
8509
  var SyncStateError = class _SyncStateError extends Error {
8445
8510
  constructor(message) {