Skip to content

feat: add Workflow Insight plugin - #661

Open
wangyb-A wants to merge 5 commits into
mainfrom
workflow-insight-plugin
Open

feat: add Workflow Insight plugin#661
wangyb-A wants to merge 5 commits into
mainfrom
workflow-insight-plugin

Conversation

@wangyb-A

Copy link
Copy Markdown
Contributor

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Issue Link, if available

N/A. Workflow Insight conformance requirements are being reviewed in aws/aws-durable-execution-conformance-tests#73.

Description

Adds the preview Workflow Insight plugin as a new insight-plugin Maven module.

The plugin emits schema-versioned execution records from the existing Java plugin hooks and matches the JavaScript Workflow Insight contract. It includes:

  • deterministic per-execution sampling and on-complete, on-failure, and on-change emission modes
  • execution input/output transforms, operation filtering, error controls, and result opt-in transforms
  • top-level and full-tree operation detail
  • operations-array and operationsByName record shapes
  • per-exporter record-size truncation
  • Lambda log, Amazon S3, and CloudWatch Logs exporters
  • exporter failure isolation and flushing

The implementation uses the invocation operation snapshots, execution payloads, errors, attempts, and serialized operation results already exposed by the Java plugin API. No core hook changes are required.

Demo/Screenshots

No UI change. insight-plugin/README.md contains a usage example. The draft conformance PR linked above contains Java handlers for all 18 Workflow Insight requirements.

Checklist

  • I have filled out every section of the PR template
  • I have thoroughly tested this change

Testing

Unit Tests

Yes. The module adds 35 tests covering record construction, sampling, emission modes, replay/suspension state, operation filtering, result transforms, aggregation, truncation, exporter requests, failure isolation, and flush behavior.

mvn -q -pl insight-plugin -am test
mvn -q -pl insight-plugin -am clean verify

Both pass.

The full repository reactor also passes:

mvn -q clean verify

Integration Tests

Yes. WorkflowInsightPluginTest exercises the plugin through LocalDurableTestRunner.

The draft Workflow Insight conformance suite was also run against deployed Java 21 Lambda functions in us-west-2:

  • S3 operations-array leg: 18 passed, 0 failed
  • CloudWatch operationsByName leg: 18 passed, 0 failed

Sink-specific assertions were capability-gated and exercised by the complementary leg.

Examples

The module README includes configuration and exporter usage. Customer-facing conformance handlers are in aws/aws-durable-execution-conformance-tests#73 rather than this repository, following the repository's conformance-test ownership rule.

@wangyb-A
wangyb-A force-pushed the workflow-insight-plugin branch from 5d83fc9 to 9cb8201 Compare August 28, 2026 18:06
@wangyb-A
wangyb-A marked this pull request as ready for review August 28, 2026 18:38
@wangyb-A
wangyb-A requested a review from a team August 28, 2026 18:38
@wangyb-A
wangyb-A temporarily deployed to ai-pr-review-runtime August 28, 2026 18:38 — with GitHub Actions Inactive
@wangyb-A
wangyb-A had a problem deploying to ai-pr-review-runtime August 28, 2026 18:40 — with GitHub Actions Failure
@wangyb-A
wangyb-A temporarily deployed to ai-pr-review-runtime August 28, 2026 18:40 — with GitHub Actions Inactive
Comment thread pom.xml
Comment thread insight-plugin/src/main/java/software/amazon/lambda/durable/insight/Json.java Outdated
@github-actions

This comment has been minimized.

@zhongkechen

Copy link
Copy Markdown
Contributor

Use /ai review command to trigger AI review workflows for draft PRs

@wangyb-A
wangyb-A marked this pull request as draft August 28, 2026 19:22
@wangyb-A
wangyb-A force-pushed the workflow-insight-plugin branch from d90f91f to bdbd88f Compare August 28, 2026 21:00
@wangyb-A

Copy link
Copy Markdown
Contributor Author

/ai review

Comment thread pom.xml
Comment thread insight-plugin/src/main/java/software/amazon/lambda/durable/insight/Json.java Outdated
@github-actions

This comment has been minimized.

@wangyb-A
wangyb-A force-pushed the workflow-insight-plugin branch from bdbd88f to 24e28b8 Compare August 31, 2026 22:49
@wangyb-A

Copy link
Copy Markdown
Contributor Author

/ai review

if (state == null || !state.sampledIn) {
return;
}
emit(buildRecord(

This comment was marked as outdated.

// across multiple emissions (ON_CHANGE), so a transform that mutates its argument in place must not
// corrupt that snapshot or any later emission's view of it. Omit on throw so a failing redactor never
// leaks the raw value.
return transform.apply(Json.deepCopyContent(value));

This comment was marked as outdated.

Comment on lines +87 to +90
if (!anyResultDropped[0] && droppedOperations[0] == 0 && !droppedInput[0] && !droppedOutput[0]) {
return record;
}
return candidate(record, ops, kept, droppedOperations[0], droppedInput[0], droppedOutput[0]);

This comment was marked as outdated.

Comment on lines +347 to +349
static Object applyDataContent(Object value, boolean include, Function<Object, Object> transform) {
if (!include || value == null) {
return null;

This comment was marked as outdated.

Comment on lines +59 to +69
if (value == null
|| value instanceof String
|| value instanceof Boolean
|| value instanceof Character
|| value instanceof Enum<?>) {
return value;
}
if (value instanceof Map<?, ?> map) {
Map<Object, Object> copy = new LinkedHashMap<>();
for (Map.Entry<?, ?> e : map.entrySet()) {
copy.put(e.getKey(), deepCopyContent(e.getValue()));

This comment was marked as outdated.

Comment on lines +135 to +136
public S3Exporter build() {
return new S3Exporter(this);

This comment was marked as outdated.

return false;
}
long unsigned = fnv1a32(executionArn) & 0xffffffffL;
return (double) unsigned / 0xffffffffL < rate;

This comment was marked as outdated.

@github-actions

This comment has been minimized.

@wangyb-A
wangyb-A force-pushed the workflow-insight-plugin branch from 24e28b8 to 3b6eb77 Compare September 2, 2026 21:25
@wangyb-A

wangyb-A commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Tracking the following up items in #679

@wangyb-A
wangyb-A marked this pull request as ready for review September 2, 2026 21:25
@wangyb-A
wangyb-A temporarily deployed to ai-pr-review-runtime September 2, 2026 21:25 — with GitHub Actions Inactive
@wangyb-A
wangyb-A had a problem deploying to ai-pr-review-runtime September 2, 2026 21:59 — with GitHub Actions Failure
@wangyb-A
wangyb-A temporarily deployed to ai-pr-review-runtime September 2, 2026 21:59 — with GitHub Actions Inactive
if (state == null || !state.sampledIn) {
return;
}
emit(buildRecord(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_xryomwjxrzqt3z55wozggpsrsl

[P2] Keep ON_CHANGE I/O off checkpoint callbacks

This synchronously exports and flushes while CheckpointManager still holds its polling lock, before pollers finish and checkpoint processing is cleared. Slow S3/Logs calls therefore delay durable progress. The completed operation can also wake the handler concurrently, allowing a terminal export to race this RUNNING export; the RUNNING S3 write may finish last and overwrite the terminal object. Queue immutable/coalesced snapshots, serialize exports outside this callback, and drain them from the awaited onInvocationEnd. Add a delayed-exporter race test.

if (!anyResultDropped[0] && droppedOperations[0] == 0 && !droppedInput[0] && !droppedOutput[0]) {
return record;
}
return candidate(record, ops, kept, droppedOperations[0], droppedInput[0], droppedOutput[0]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_sulajh3ii6jyv6tb56c7lizmvp

[P2] Enforce the size limit after every truncation phase

The final candidate is returned without remeasurement. An oversized top-level execution error or another retained scalar can therefore remain above the exporter's limit after operations and payloads are removed, causing the exporter to reject the record and emit() to discard it. Remeasure the final shape and bound remaining optional fields such as error text; if no legal record fits, skip it with an explicit diagnostic. Test oversized errors in both render shapes.

// across multiple emissions (ON_CHANGE), so a transform that mutates its argument in place must not
// corrupt that snapshot or any later emission's view of it. Omit on throw so a failing redactor never
// leaks the raw value.
return transform.apply(Json.deepCopyContent(value));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_j5amytymbyibhzrtph7noqfti6

[P2] Preserve runtime types passed to content transforms

deepCopyContent converts DTOs into maps and Java-time values into strings before invoking the transform; input has already undergone the same normalization when cached. A transform that casts the execution input/output to its Java type consequently throws, and the catch silently omits that field. Create type-preserving defensive snapshots for transform invocation, then normalize only the transform result. Add DTO, Java-time, and custom-SerDes transform tests.

Comment on lines +348 to +349
if (!include || value == null) {
return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_oirdg3rnpdnwvpldafyiqrnncz

[P2] Preserve included JSON null values

An enabled value that is actually null is represented identically to excluded or unavailable content, and the wire map subsequently omits it. Thus a successful handler returning null has no output field despite output inclusion being enabled. Track field presence separately from value and emit JSON null when included; omit only excluded or unavailable fields. Add null input/output wire-shape tests.

// content transform can mutate it. This raw, detached snapshot is the single source of truth for input on
// every emission (start / change / end); each build hands transforms a separate defensive copy so a
// mutating transform cannot corrupt it.
state.cachedInput = Json.deepCopyContent(info.executionInput());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_65fn2ndq4okrooanfbimyf6a6m

[P2] Do not snapshot input when input collection is disabled

This always runs the input through the plugin's Jackson mapper, even for content.input(false). With a custom-SerDes value that Jackson cannot copy, the start hook throws before the ON_CHANGE start record is emitted, although the input was explicitly excluded; large excluded inputs also incur unnecessary serialization cost. Check includeInput before snapshotting and cache explicit absence without inspecting the value. Add an ON_CHANGE test using an excluded, non-Jackson input.

Comment on lines +135 to +136
public S3Exporter build() {
return new S3Exporter(this);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_5cs3isxmbcgkzlluwxepe4qxv4

[P3] Reject invalid exporter configuration at build time

The builder accepts a null/blank bucket and non-positive record limits. Missing destinations fail only during export, where the plugin catches the exception and silently produces no record; non-positive limits disable truncation. Validate required destinations and positive limits before creating the client, apply equivalent validation to CloudWatch Logs and Lambda log exporters, and add invalid-configuration tests.

Comment on lines +63 to +69
|| value instanceof Enum<?>) {
return value;
}
if (value instanceof Map<?, ?> map) {
Map<Object, Object> copy = new LinkedHashMap<>();
for (Map.Entry<?, ?> e : map.entrySet()) {
copy.put(e.getKey(), deepCopyContent(e.getValue()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_rzwdoyquezpr2qgby63imtmxcn

[P3] Detach enums and map keys between exporters

The copy still shares enum instances and every map key. Enums may expose mutable state or a mutable @JsonValue, while keys are reachable through toWireMap(); mutation by one exporter can therefore alter later exporters' records. Convert enums to detached JSON scalars and copy keys using the mapper's key-serialization semantics. Add mutable-enum and mutable-key isolation tests.

return false;
}
long unsigned = fnv1a32(executionArn) & 0xffffffffL;
return (double) unsigned / 0xffffffffL < rate;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_47kz274xvx4sow4mmbm7gzor7a

[P3] Normalize sampling into the documented half-open interval

Dividing by 0xffffffff maps the maximum unsigned hash to exactly 1.0, contradicting the documented [0,1) range and shifting deterministic boundary decisions. Divide by 2^32 (0x1_0000_0000L) and add shared fixed-vector tests, coordinating the boundary change with the other SDK implementations.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Codex AI review

Found 8 actionable issues, primarily around ON_CHANGE lifecycle races and silent record loss during truncation/content handling. Additional issues affect excluded inputs, exporter validation/isolation, and sampling boundaries.

Reviewed commit 3b6eb771df61db642c999e1d308344c97b3cf686. Workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants