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]