review-workspace 1.9.0 → 1.10.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.
@@ -68,6 +68,19 @@ export function resolveTarget(target, patch) {
68
68
  }
69
69
  return undefined;
70
70
  }
71
+ // A Verification item's targetIds may name an Annotation id, an Evidence id, or a file
72
+ // path directly (see render.ts's buildVerificationByTargetId) — none of which is itself
73
+ // a guarantee of a concrete place. An Evidence id only anchors if that Evidence's own
74
+ // targetIds resolve to an Annotation or a file in turn; anything else (a Behavioral Group
75
+ // id, a dangling id) resolves to nowhere the reviewer can be sent.
76
+ function isConcreteAnchor(id, annotationIds, evidenceById, filePaths) {
77
+ if (annotationIds.has(id) || filePaths.has(id))
78
+ return true;
79
+ const evidence = evidenceById.get(id);
80
+ if (!evidence)
81
+ return false;
82
+ return (evidence.targetIds ?? []).some((innerId) => annotationIds.has(innerId) || filePaths.has(innerId));
83
+ }
71
84
  export function collectDiagnostics(document, patch, bundlePath, limits = DEFAULT_LIMITS) {
72
85
  const diagnostics = [];
73
86
  const schemaResult = validateReviewDocumentSchema(document);
@@ -110,5 +123,20 @@ export function collectDiagnostics(document, patch, bundlePath, limits = DEFAULT
110
123
  }
111
124
  }
112
125
  }
126
+ if (document.verification?.length) {
127
+ const annotationIds = new Set((document.annotations ?? []).map((a) => a.id));
128
+ const evidenceById = new Map((document.evidence ?? []).map((e) => [e.id, e]));
129
+ const filePaths = new Set(patch.files.map((f) => f.path));
130
+ for (const item of document.verification) {
131
+ if (item.status !== 'gap' || item.diffuse)
132
+ continue;
133
+ const ids = item.targetIds ?? [];
134
+ const anchored = ids.some((id) => isConcreteAnchor(id, annotationIds, evidenceById, filePaths));
135
+ if (!anchored) {
136
+ const detail = ids.length === 0 ? 'targetIds is empty' : `targetIds ${JSON.stringify(ids)} resolve to no Annotation, Evidence, or file`;
137
+ diagnostics.push({ kind: 'unanchored-verification-gap', verificationId: item.id, detail });
138
+ }
139
+ }
140
+ }
113
141
  return diagnostics;
114
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "review-workspace",
3
- "version": "1.9.0",
3
+ "version": "1.10.2",
4
4
  "description": "A local, offline decision surface for reviewing a code Comparison — evidence-linked map of intent, behavior, risk, and observable effects.",
5
5
  "license": "MIT",
6
6
  "author": "Salih Sayer",
@@ -154,7 +154,8 @@
154
154
  "id": { "type": "string", "minLength": 1 },
155
155
  "description": { "type": "string", "minLength": 1 },
156
156
  "status": { "enum": ["unverified", "verified", "gap"] },
157
- "targetIds": { "type": "array", "items": { "type": "string" } }
157
+ "targetIds": { "type": "array", "items": { "type": "string" } },
158
+ "diffuse": { "type": "boolean" }
158
159
  }
159
160
  },
160
161
  "answer": {