luigidemasi commented on code in PR #150:
URL: https://github.com/apache/camel-kit/pull/150#discussion_r4493058638
##########
kit/camel-kit-core/src/main/java/org/apache/camel/kit/ship/controller/ShipStageResult.java:
##########
@@ -280,6 +281,141 @@ private static void requireReport(String report) throws
IOException {
utf8(report, MAX_REPORT_BYTES, "stage report");
}
+ private static String extractJsonObject(String text) throws IOException {
+ if (text == null) {
+ return text;
+ }
+ String stripped = text.strip();
+ if (stripped.startsWith("{")) {
+ return stripped;
+ }
+ String fenced = extractFencedJson(text);
+ if (fenced != null) {
+ return fenced;
+ }
+ int start = text.indexOf('{');
+ int end = text.lastIndexOf('}');
+ if (start >= 0 && end > start) {
+ String prefix = text.substring(0, start).strip();
+ if (!prefix.isEmpty() && prefix.indexOf('[') >= 0) {
+ throw new IOException("Pi stage result is wrapped in an
array");
+ }
+ return text.substring(start, end + 1);
+ }
+ return stripped;
+ }
+
+ private static String extractFencedJson(String text) throws IOException {
+ String result = null;
+ int regionStart = -1;
+ int regionEnd = -1;
+ int pos = 0;
+ int len = text.length();
+
+ while (pos < len) {
+ int tick = text.indexOf("```", pos);
+ if (tick < 0) {
+ break;
+ }
+ if (tick > 0 && text.charAt(tick - 1) != '\n') {
+ pos = tick + 3;
+ continue;
+ }
+ int cur = tick + 3;
+ if (cur + 4 <= len && text.startsWith("json", cur)) {
+ cur += 4;
+ }
+ while (cur < len && (text.charAt(cur) == ' ' || text.charAt(cur)
== '\t')) {
+ cur++;
+ }
+ if (cur >= len || text.charAt(cur) != '\n') {
+ pos = tick + 3;
+ continue;
+ }
+ int contentStart = cur + 1;
+
+ int close = contentStart;
+ boolean found = false;
+ while (true) {
+ close = text.indexOf("\n```", close);
+ if (close < 0) {
+ break;
+ }
+ int afterClose = close + 4;
+ if (afterClose >= len || text.charAt(afterClose) == '\n') {
+ found = true;
+ break;
+ }
+ close = afterClose;
+ }
+ if (!found) {
+ break;
+ }
+
+ String content = text.substring(contentStart, close).strip();
+ int end = close + 4;
+ if (end < len && text.charAt(end) == '\n') {
+ end++;
+ }
+
+ if (content.startsWith("{") && content.endsWith("}")) {
+ if (result != null) {
+ throw new IOException("Pi stage result contains
conflicting fenced JSON envelopes");
+ }
+ result = content;
+ regionStart = tick;
+ regionEnd = end;
+ }
+ pos = end;
+ }
+
+ if (result == null) {
+ return null;
+ }
+ String before = text.substring(0, regionStart);
+ String after = text.substring(regionEnd);
+ if (hasBareJsonStart(before) || hasBareJsonStart(after)) {
+ throw new IOException("Pi stage result contains conflicting JSON
envelopes");
+ }
+ return result;
+ }
+
+ private static boolean hasBareJsonStart(String fragment) {
+ int i = 0;
+ int len = fragment.length();
+ while (i < len) {
+ char c = fragment.charAt(i);
+ if (c == '`') {
+ int j = i + 1;
+ while (j < len && fragment.charAt(j) != '`' &&
fragment.charAt(j) != '\n') {
+ j++;
+ }
+ if (j < len && fragment.charAt(j) == '`') {
+ String span = fragment.substring(i + 1, j);
+ if (looksLikeEnvelope(span)) {
+ return true;
+ }
+ i = j + 1;
+ continue;
+ }
+ }
+ if (c == '{') {
+ return true;
+ }
+ i++;
+ }
+ return false;
+ }
+
+ private static boolean looksLikeEnvelope(String span) {
+ try {
+ JsonNode node = JSON.readTree(span);
+ return node.isObject() && node.has("materialAmbiguity");
+ } catch (JsonProcessingException e) {
+ return false;
Review Comment:
[P2] Keep malformed inline envelope candidates fail-closed
Jackson now correctly recognizes escaped field names, but a parse error is
treated as evidence that the span is harmless. The caller then skips that whole
span. For example, take the corrected DESIGN envelope from the existing test,
duplicate its `"materialAmbiguity":true` member, and put it in backticks after
the earlier no-question fenced result. `STRICT_DUPLICATE_DETECTION` throws
here, the catch returns false, and the correction is discarded. Putting two
complete JSON result documents in that same inline span has the same effect via
`FAIL_ON_TRAILING_TOKENS`.
I reproduced both through the real coordinator with its Pi RPC fixture:
DESIGN completes with `materialAmbiguity=false`, zero questions, and PLAN
starts (one attempt). The same malformed inputs supplied raw fail DESIGN with
zero PLAN attempts; the previous head `91d9da08` and base `4edcf09f` also
reject the mixed responses. A valid raw correction still pauses under SMART.
This establishes unwanted stage advancement, not workflow completion or
publication.
Please distinguish harmless inline content from malformed/conflicting JSON
candidates so parser errors cannot suppress the conflict check. Retain
decoded-key detection and the harmless status example, and add duplicate-key
and trailing-document mixed-response regressions with their raw rejection
controls. These are two manifestations of the same fail-open branch.
_AI-generated by Codex on behalf of @luigidemasi._
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]