luigidemasi commented on code in PR #150:
URL: https://github.com/apache/camel-kit/pull/150#discussion_r4481276478


##########
kit/camel-kit-core/src/main/java/org/apache/camel/kit/ship/controller/ShipStageResult.java:
##########
@@ -280,6 +282,110 @@ 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) {
+                pos = contentStart;
+                continue;

Review Comment:
   [P2] Avoid rescanning the remaining input for each unfinished fence
   
   The exact earlier input is faster, but adding a newline after each `{` still 
makes this loop quadratic: `"```json\n{\n".repeat(90_000)` is 900 KB and 
exceeded a 12-second process limit. Each unsuccessful closing-fence search 
traverses the remaining openers, then `pos = contentStart` retries that same 
suffix. The base parser rejects the same input in about 0.21 s.
   
   There is also a new quadratic path in `BARE_JSON_START`: 
`"\n".repeat(100_000)` before one valid fenced envelope exceeds 12 s, versus 
about 0.22 s on `352b4589`, because `\s*` repeatedly spans the remaining 
newlines. Both inputs are below the 1 MiB limit.
   
   Please keep a monotonically advancing scan, stop/reject after a suffix has 
no closing fence, and avoid newline-spanning backtracking in the outside-fence 
check. Add both failing inputs to the regression coverage: replacing only the 
scanner with the previous regex while retaining the current outside-fence check 
still passes all 19 parser tests, including 
`malformedFenceOpenersDoNotCauseQuadraticScan`.
   
   _AI-generated by Codex on behalf of @luigidemasi._
   



##########
kit/camel-kit-core/src/main/java/org/apache/camel/kit/ship/controller/ShipStageResult.java:
##########
@@ -280,6 +282,110 @@ 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) {
+                pos = contentStart;
+                continue;
+            }
+
+            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;

Review Comment:
   [P2] Reject a second stage envelope regardless of line placement
   
   Restricting `hasBareJsonStart()` to a line-start brace reopens the ambiguity 
regression when a correction is written as `Correction: { ... }`. With a fenced 
DESIGN result containing `materialAmbiguity: false`, followed by a same-line 
bare correction containing `materialAmbiguity: true` and an unanswered 
question, this check returns false and the correction is discarded. The real 
coordinator/Pi RPC fixture completes DESIGN with zero questions and starts PLAN 
under SMART; the correction alone pauses, and `352b4589` rejects the mixed 
response before PLAN starts.
   
   Please retain support for the inline prose example without silently 
accepting a second stage-result envelope. Validate a wrapper grammar that 
cannot discard another envelope based on its line position, and add this 
same-line mixed-response regression. The fixture proves stage advancement, not 
full workflow completion or publication.
   
   _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]

Reply via email to