feat(javascript): report observed export property presence (#1160)

* feat(javascript): report observed export property presence

Extend compare_javascript_export_shapes so each retained return variant
lists observed property names and each change reports presence independently
of value resolution. Complete parent coverage classifies one-sided names as
added or removed even when static values stay unknown.

Fixes #725

* test(javascript): match unknown export-shape values by availability

Nested availability objects include a reason when values stay unresolved.
Assert availability through objectContaining so presence-only added and
removed rows still verify unknown values.

* fix(javascript): preserve uncertain export property presence and locations

* fix(javascript): normalize presence and preserve local property coverage

* fix(javascript): require present discriminants for return pairing

* fix(javascript): normalize retained shapes before comparison

* refactor(javascript): share return projection with executable examples

* fix(javascript): localize uncertainty for missing mutated slots

* fix(javascript): bind advertised export examples to exact artifacts

* fix(javascript): keep uncertain property flow as candidates

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
Matt Van Hornandmorluto authored and GitHub committed 2026-10-09 18:30:31 +08:00
1 parent 04aef6df33
commit 45e3edc17f
25 files changed
+1552 -113

No files matched your search

@@ -73,7 +73,10 @@ export const compareJavaScriptExportShapesInputSchema = z
});
});
const projectedReturnFieldShape = { path: jsonPointerSchema };
const projectedReturnFieldShape = {
path: jsonPointerSchema,
presence: z.enum(["present", "absent", "unknown-coverage"]).optional(),
};
const projectedReturnFieldSchema = z.discriminatedUnion("state", [
z.strictObject({
...projectedReturnFieldShape,
@@ -229,11 +232,21 @@ const discriminantSchema = z.strictObject({
value: semanticPrimitiveSchema,
});
const propertyPresenceSchema = z.enum([
"present",
"absent",
"unknown-coverage",
]);
const comparisonChangeSchema = z.strictObject({
change_id: prefixedDigestSchema("jesc_change"),
status: z.enum(["added", "removed", "changed", "unknown"]),
path: jsonPointerSchema,
discriminant: discriminantSchema.nullable(),
presence: z.strictObject({
left: propertyPresenceSchema,
right: propertyPresenceSchema,
}),
left: valueAvailabilitySchema,
right: valueAvailabilitySchema,
left_source_range: sourceRangeSchema.nullable(),
@@ -242,11 +255,31 @@ const comparisonChangeSchema = z.strictObject({
limitations: z.array(textSchema),
});
/** One change row; presence is required even when values stay unknown. */
export const javaScriptExportShapeComparisonChangeSchema =
comparisonChangeSchema;
const inventoryPropertyCoverageSchema = z.strictObject({
path: jsonPointerSchema,
status: z.enum(["complete", "partial"]),
});
const propertyInventorySchema = z.strictObject({
side: z.enum(["left", "right"]),
variant_index: z.number().int().nonnegative(),
discriminant: discriminantSchema.nullable(),
paired: z.boolean(),
source_range: sourceRangeSchema,
properties: z.array(jsonPointerSchema),
property_coverage: z.array(inventoryPropertyCoverageSchema),
});
/** Static export-return comparison with explicit unknown semantics. */
export const javaScriptExportShapeComparisonResultSchema = z.strictObject({
comparison_id: prefixedDigestSchema("jesc"),
left: selectorResultSchema,
right: selectorResultSchema,
property_inventories: z.array(propertyInventorySchema),
summary: z.strictObject({
added: z.number().int().min(0),
removed: z.number().int().min(0),
@@ -295,3 +328,6 @@ export type JavaScriptExportShapeComparisonResult = z.output<
export type JavaScriptExportShapeComparisonChange = z.output<
typeof comparisonChangeSchema
>;
export type JavaScriptExportShapePropertyInventory = z.output<
typeof propertyInventorySchema
>;