@c4a/extract 0.6.1 → 0.6.2

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/index.js +122 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -10,7 +10,7 @@ project extraction phases such as `extractTs({ source, collection: "codegraph" }
10
10
  - Language plugins implement `ExtractionPlugin` and return `ExtractionResult` v2.
11
11
  - The runner loads one or more plugins, scans repository modules, emits progress/module-error/summary events, and can build an optional low-level code snapshot payload.
12
12
  - `@c4a/context-cli` invokes the runner from `context run extract:<source>:codegraph` after the workspace declares an `extractTs` phase.
13
- - The project flow stages review candidates under `unapproved/entities.jsonl` and keeps runtime candidate artifacts under `.tmp/context-runtime/extract/`; approved Markdown is written only by review/apply.
13
+ - The project flow stages review candidates under `.tmp/context-runtime/lifecycle/candidates.jsonl` and keeps runtime candidate artifacts under `.tmp/context-runtime/extract/`; approved Markdown is written only by review/apply.
14
14
 
15
15
  **Depends on:** `@c4a/core`, `web-tree-sitter`, `zod`
16
16
 
package/index.js CHANGED
@@ -13721,7 +13721,115 @@ var generateSymbolDiff = (current, previous) => {
13721
13721
  // src/documentEvidence.ts
13722
13722
  import { Buffer as Buffer2 } from "node:buffer";
13723
13723
  import { createHash as createHash2 } from "node:crypto";
13724
- var DOCUMENT_EVIDENCE_NORMALIZER_VERSION = "document-evidence-normalizer.v1";
13724
+
13725
+ // src/documentCaptureFidelity.ts
13726
+ function isRecord(value) {
13727
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13728
+ }
13729
+ function requiredString(value, field) {
13730
+ if (typeof value !== "string" || value.trim().length === 0) {
13731
+ throw new TypeError(`${field} must be a non-empty string`);
13732
+ }
13733
+ return value.trim();
13734
+ }
13735
+ function counts(value, field) {
13736
+ if (!isRecord(value))
13737
+ throw new TypeError(`${field} must be an object`);
13738
+ const result = {};
13739
+ for (const [key, item] of Object.entries(value)) {
13740
+ if (key.trim().length === 0 || !Number.isInteger(item) || item < 0) {
13741
+ throw new TypeError(`${field}.${key} must be a non-negative integer`);
13742
+ }
13743
+ result[key] = item;
13744
+ }
13745
+ return result;
13746
+ }
13747
+ function skippedReasons(value, field) {
13748
+ if (!Array.isArray(value))
13749
+ throw new TypeError(`${field} must be an array`);
13750
+ return value.map((item, index) => {
13751
+ if (!isRecord(item))
13752
+ throw new TypeError(`${field}[${index}] must be an object`);
13753
+ if (!Number.isInteger(item.count) || item.count < 1) {
13754
+ throw new TypeError(`${field}[${index}] must include block_type, positive count, and reason`);
13755
+ }
13756
+ return {
13757
+ block_type: requiredString(item.block_type, `${field}[${index}].block_type`),
13758
+ count: item.count,
13759
+ reason: requiredString(item.reason, `${field}[${index}].reason`)
13760
+ };
13761
+ });
13762
+ }
13763
+ function fidelityIssues(value, field) {
13764
+ if (!Array.isArray(value))
13765
+ throw new TypeError(`${field} must be an array`);
13766
+ return value.map((item, index) => {
13767
+ if (!isRecord(item))
13768
+ throw new TypeError(`${field}[${index}] must be an object`);
13769
+ if (item.severity !== "warning" && item.severity !== "error" || !Number.isInteger(item.count) || item.count < 1) {
13770
+ throw new TypeError(`${field}[${index}] must include severity, code, block_type, positive count, and reason`);
13771
+ }
13772
+ return {
13773
+ severity: item.severity,
13774
+ impact: item.impact === "evidence" || item.impact === "projection" ? item.impact : item.severity === "error" ? "evidence" : "projection",
13775
+ code: requiredString(item.code, `${field}[${index}].code`),
13776
+ block_type: requiredString(item.block_type, `${field}[${index}].block_type`),
13777
+ count: item.count,
13778
+ reason: requiredString(item.reason, `${field}[${index}].reason`)
13779
+ };
13780
+ });
13781
+ }
13782
+ function parseDocumentCaptureFidelity(value, field) {
13783
+ if (value === undefined)
13784
+ return;
13785
+ if (!isRecord(value))
13786
+ throw new TypeError(`${field} must be an object`);
13787
+ if (value.status !== "complete" && value.status !== "warning" && value.status !== "error") {
13788
+ throw new TypeError(`${field}.status must be complete, warning, or error`);
13789
+ }
13790
+ const discovered = counts(value.discovered, `${field}.discovered`);
13791
+ const converted = counts(value.converted, `${field}.converted`);
13792
+ const skipped = skippedReasons(value.skipped, `${field}.skipped`);
13793
+ const issues = fidelityIssues(value.issues, `${field}.issues`);
13794
+ const countedBlockTypes = new Set([
13795
+ ...Object.keys(discovered),
13796
+ ...Object.keys(converted),
13797
+ ...skipped.map((item) => item.block_type)
13798
+ ]);
13799
+ for (const blockType of countedBlockTypes) {
13800
+ const discoveredCount = discovered[blockType] ?? 0;
13801
+ const skippedCount = skipped.filter((item) => item.block_type === blockType).reduce((sum, item) => sum + item.count, 0);
13802
+ if ((converted[blockType] ?? 0) + skippedCount !== discoveredCount) {
13803
+ throw new TypeError(`${field} does not close for ${blockType}: discovered ${discoveredCount}, converted ${converted[blockType] ?? 0}, skipped ${skippedCount}`);
13804
+ }
13805
+ }
13806
+ const evidenceStatus = issues.some((issue) => issue.impact === "evidence" && issue.severity === "error") ? "error" : "complete";
13807
+ const projectionIssues = issues.filter((issue) => issue.impact === "projection");
13808
+ const inferredProjectionStatus = projectionIssues.some((issue) => issue.severity === "error") ? "error" : projectionIssues.some((issue) => issue.code === "lark.capture.generic-projection") ? "generic" : projectionIssues.length > 0 ? "warning" : "complete";
13809
+ const projectionStatus = value.projection_status === "complete" || value.projection_status === "generic" || value.projection_status === "warning" || value.projection_status === "error" ? value.projection_status : inferredProjectionStatus;
13810
+ const status = evidenceStatus === "error" || projectionStatus === "error" ? "error" : issues.length > 0 ? "warning" : "complete";
13811
+ if (value.status !== status) {
13812
+ throw new TypeError(`${field}.status must be ${status} for its issues`);
13813
+ }
13814
+ if (value.evidence_status !== undefined && value.evidence_status !== evidenceStatus) {
13815
+ throw new TypeError(`${field}.evidence_status must be ${evidenceStatus} for its issues`);
13816
+ }
13817
+ if (value.projection_status !== undefined && value.projection_status !== inferredProjectionStatus) {
13818
+ throw new TypeError(`${field}.projection_status must be ${inferredProjectionStatus} for its issues`);
13819
+ }
13820
+ return {
13821
+ status,
13822
+ evidence_status: evidenceStatus,
13823
+ projection_status: projectionStatus,
13824
+ discovered,
13825
+ converted,
13826
+ skipped,
13827
+ issues
13828
+ };
13829
+ }
13830
+
13831
+ // src/documentEvidence.ts
13832
+ var DOCUMENT_EVIDENCE_NORMALIZER_VERSION = "document-evidence-normalizer.v2";
13725
13833
  var DOCUMENT_SNAPSHOT_MANIFEST_SCHEMA_VERSION = "document.snapshot.v2";
13726
13834
  var DEFAULT_SOURCE_SPAN_HASH_LENGTH = 12;
13727
13835
  var BOM = "\uFEFF";
@@ -13729,7 +13837,7 @@ var HASH_ID_RE = /^(?:sha256:)?[a-f0-9]{64}$/u;
13729
13837
  var SOURCE_SPAN_HASH_RE = /^[a-f0-9]{8,64}$/u;
13730
13838
  var DOCUMENT_SOURCE_SLUG_RE = /^[a-z0-9][a-z0-9._-]*$/u;
13731
13839
  var DOCUMENT_SOURCE_BATCH_RE = /^\d{8}\/[a-z0-9][a-z0-9._-]*$/u;
13732
- function isRecord(value) {
13840
+ function isRecord2(value) {
13733
13841
  return value !== null && typeof value === "object" && !Array.isArray(value);
13734
13842
  }
13735
13843
  function bytesOf(value) {
@@ -14032,7 +14140,7 @@ function createDocumentSnapshotManifest(input) {
14032
14140
  });
14033
14141
  }
14034
14142
  function parseFileEntry(value, index) {
14035
- if (!isRecord(value)) {
14143
+ if (!isRecord2(value)) {
14036
14144
  throw new TypeError(`snapshot manifest files[${index}] must be an object`);
14037
14145
  }
14038
14146
  if (typeof value.path !== "string") {
@@ -14060,7 +14168,7 @@ function parseFileEntry(value, index) {
14060
14168
  };
14061
14169
  }
14062
14170
  function parseAssetEntry(value, index) {
14063
- if (!isRecord(value)) {
14171
+ if (!isRecord2(value)) {
14064
14172
  throw new TypeError(`snapshot manifest assets[${index}] must be an object`);
14065
14173
  }
14066
14174
  if (typeof value.path !== "string") {
@@ -14079,7 +14187,7 @@ function parseAssetEntry(value, index) {
14079
14187
  function parseStringRecord(value, field) {
14080
14188
  if (value === undefined)
14081
14189
  return;
14082
- if (!isRecord(value)) {
14190
+ if (!isRecord2(value)) {
14083
14191
  throw new TypeError(`${field} must be an object`);
14084
14192
  }
14085
14193
  const result = {};
@@ -14126,7 +14234,7 @@ function optionalRouteFiles(value, field) {
14126
14234
  throw new TypeError(`${field} must be an array`);
14127
14235
  }
14128
14236
  const result = value.map((item, index) => {
14129
- if (!isRecord(item)) {
14237
+ if (!isRecord2(item)) {
14130
14238
  throw new TypeError(`${field}[${index}] must be an object`);
14131
14239
  }
14132
14240
  const path2 = optionalMetadataString(item.path, `${field}[${index}].path`);
@@ -14148,7 +14256,7 @@ function optionalRouteHints(value, field) {
14148
14256
  throw new TypeError(`${field} must be an array`);
14149
14257
  }
14150
14258
  const result = value.map((item, index) => {
14151
- if (!isRecord(item)) {
14259
+ if (!isRecord2(item)) {
14152
14260
  throw new TypeError(`${field}[${index}] must be an object`);
14153
14261
  }
14154
14262
  const documentPath = optionalMetadataString(item.documentPath, `${field}[${index}].documentPath`);
@@ -14168,12 +14276,12 @@ function optionalRouteHints(value, field) {
14168
14276
  function parseManifestMetadata(value) {
14169
14277
  if (value === undefined)
14170
14278
  return;
14171
- if (!isRecord(value)) {
14279
+ if (!isRecord2(value)) {
14172
14280
  throw new TypeError("snapshot manifest metadata must be an object");
14173
14281
  }
14174
14282
  let source2;
14175
14283
  if (value.source !== undefined) {
14176
- if (!isRecord(value.source)) {
14284
+ if (!isRecord2(value.source)) {
14177
14285
  throw new TypeError("snapshot manifest metadata.source must be an object");
14178
14286
  }
14179
14287
  const url = optionalMetadataString(value.source.url, "snapshot manifest metadata.source.url");
@@ -14193,18 +14301,20 @@ function parseManifestMetadata(value) {
14193
14301
  }
14194
14302
  let capture;
14195
14303
  if (value.capture !== undefined) {
14196
- if (!isRecord(value.capture)) {
14304
+ if (!isRecord2(value.capture)) {
14197
14305
  throw new TypeError("snapshot manifest metadata.capture must be an object");
14198
14306
  }
14199
14307
  const include = optionalMetadataStringArray(value.capture.include, "snapshot manifest metadata.capture.include");
14200
14308
  const documentExtensions = optionalMetadataStringArray(value.capture.documentExtensions, "snapshot manifest metadata.capture.documentExtensions");
14201
14309
  const routeFiles = optionalRouteFiles(value.capture.routeFiles, "snapshot manifest metadata.capture.routeFiles");
14202
14310
  const routeHints = optionalRouteHints(value.capture.routeHints, "snapshot manifest metadata.capture.routeHints");
14311
+ const fidelity = parseDocumentCaptureFidelity(value.capture.fidelity, "snapshot manifest metadata.capture.fidelity");
14203
14312
  capture = {
14204
14313
  ...include !== undefined ? { include } : {},
14205
14314
  ...documentExtensions !== undefined ? { documentExtensions } : {},
14206
14315
  ...routeFiles !== undefined ? { routeFiles } : {},
14207
- ...routeHints !== undefined ? { routeHints } : {}
14316
+ ...routeHints !== undefined ? { routeHints } : {},
14317
+ ...fidelity !== undefined ? { fidelity } : {}
14208
14318
  };
14209
14319
  if (Object.keys(capture).length === 0)
14210
14320
  capture = undefined;
@@ -14215,7 +14325,7 @@ function parseManifestMetadata(value) {
14215
14325
  } : {};
14216
14326
  }
14217
14327
  function parseDocumentSnapshotManifest(value) {
14218
- if (!isRecord(value)) {
14328
+ if (!isRecord2(value)) {
14219
14329
  throw new TypeError("snapshot manifest must be an object");
14220
14330
  }
14221
14331
  if (value.schema_version !== DOCUMENT_SNAPSHOT_MANIFEST_SCHEMA_VERSION) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/extract",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "type": "module",
5
5
  "description": "Code extraction engine — Tree-sitter AST parsing and structural analysis",
6
6
  "license": "MIT",