feat!: return success for pending and running jobs #13

Merged
jercik merged 3 commits from feat/nonfailed-job-status into main 2026-08-26 13:39:11 +00:00
Owner

Pending and running envelopes now exit 0, so generic wrappers and set -e stop misclassifying valid work as failures; .job.status decides whether to poll. The client also rejects contradictory snapshots and prints a credential-safe poll command. Release only after tropkod#30 is deployed.

Pending and running envelopes now exit `0`, so generic wrappers and `set -e` stop misclassifying valid work as failures; `.job.status` decides whether to poll. The client also rejects contradictory snapshots and prints a credential-safe poll command. Release only after [tropkod#30](https://code.j4k.dev/j4k/tropkod/pulls/30) is deployed.
feat!: treat nonfailed jobs as successful
All checks were successful
PR Review / Prepare immutable review tools (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-smart-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-2 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-3 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-smart-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna-2 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna-3 generator (pull_request_target) Has been skipped
PR Review / Dispatch and observe exact review writers (pull_request_target) Has been skipped
PR Review / Request trusted main review (pull_request_target) Successful in 5s
commit-msg / commitlint (pull_request) Successful in 28s
Checks / quality-checks (26.5.0) (pull_request) Successful in 1m2s
Checks / quality-checks (24.15.0) (pull_request) Successful in 1m3s
9a097105b9
forgejo-actions left a comment

Approach review: The exit-code and polling-output direction is sound, but contradictory snapshots should be modeled as lifecycle variants in the schema instead of a parallel imperative validator. This would make the parsed TypeScript type preserve the same guarantees enforced at runtime and reduce the maintenance cost of adding states or fields.

Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Approach review:** The exit-code and polling-output direction is sound, but contradictory snapshots should be modeled as lifecycle variants in the schema instead of a parallel imperative validator. This would make the parsed TypeScript type preserve the same guarantees enforced at runtime and reduce the maintenance cost of adding states or fields. _Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1hcHByb2FjaC1sdW5hLTIiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzI5OTAiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiNzlkMTg0MDYtMjBkMC00YTM1LThjNDAtNWVkZjE4MGVjNTQzIn0= -->
@ -154,0 +155,4 @@
job: JobRecord,
analysis: Analysis.nullish().transform((value) => value ?? undefined),
})
.superRefine(validateQuerySubmission);

Model the lifecycle as Zod/TypeScript variants here—for example, a discriminated JobRecord plus envelope variants pairing pending/running/failed/completed jobs with their permitted analysis—and reserve superRefine for cross-record ID equality checks. The broad object plus validateQuerySubmission rejects impossible snapshots only at runtime, while its inferred type still permits unrelated optional analysis_id, error, and analysis fields. That creates two lifecycle definitions to maintain and discards the knowledge gained during parsing; the existing z.discriminatedUnion pattern is the clearer fit.

Model the lifecycle as Zod/TypeScript variants here—for example, a discriminated `JobRecord` plus envelope variants pairing pending/running/failed/completed jobs with their permitted `analysis`—and reserve `superRefine` for cross-record ID equality checks. The broad object plus `validateQuerySubmission` rejects impossible snapshots only at runtime, while its inferred type still permits unrelated optional `analysis_id`, `error`, and `analysis` fields. That creates two lifecycle definitions to maintain and discards the knowledge gained during parsing; the existing `z.discriminatedUnion` pattern is the clearer fit.
Author
Owner

Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.

<!-- gh-feedback:reply-to:69865 --> Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.
jercik marked this conversation as resolved
forgejo-actions left a comment

Approach review: Returning 0 for valid nonterminal jobs and printing a credential-free poll command are appropriate. The contradictory-snapshot handling should instead model lifecycle variants in the Zod schema so parsing yields a discriminated domain type and protocol evolution has one source of truth.

Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Approach review:** Returning `0` for valid nonterminal jobs and printing a credential-free poll command are appropriate. The contradictory-snapshot handling should instead model lifecycle variants in the Zod schema so parsing yields a discriminated domain type and protocol evolution has one source of truth. _Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1hcHByb2FjaC1sdW5hLTMiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzI5OTAiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiNjQyNWVjMTAtMWFkYy00MDNkLWJkODItMmU1OGFkN2VkMDJhIn0= -->
@ -154,0 +155,4 @@
job: JobRecord,
analysis: Analysis.nullish().transform((value) => value ?? undefined),
})
.superRefine(validateQuerySubmission);

The lifecycle combinations are a natural schema-union concern rather than a superRefine concern. Define pending/running/completed/failed job variants (plus envelope variants for whether analysis is present), then retain a small refinement only for cross-record ID and question equality. The current approach duplicates the schema in SubmissionForValidation, walks every state imperatively, and still infers analysis_id and error as independent optionals, so parsing discards the exact state knowledge it just proved. A Zod union/discriminated union would remove most of this validator and make future protocol states materially safer to add.

The lifecycle combinations are a natural schema-union concern rather than a `superRefine` concern. Define pending/running/completed/failed job variants (plus envelope variants for whether `analysis` is present), then retain a small refinement only for cross-record ID and question equality. The current approach duplicates the schema in `SubmissionForValidation`, walks every state imperatively, and still infers `analysis_id` and `error` as independent optionals, so parsing discards the exact state knowledge it just proved. A Zod union/discriminated union would remove most of this validator and make future protocol states materially safer to add.
Author
Owner

Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.

<!-- gh-feedback:reply-to:69871 --> Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.
jercik marked this conversation as resolved
forgejo-actions left a comment

Approach review: The exit-code and polling-command changes are appropriately small, but the response lifecycle has a materially simpler model. Encode the status-specific shapes in Zod and reserve refinement for cross-record identity checks; that removes duplicated lifecycle logic and makes the inferred type reflect the states the parser accepts.

Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Approach review:** The exit-code and polling-command changes are appropriately small, but the response lifecycle has a materially simpler model. Encode the status-specific shapes in Zod and reserve refinement for cross-record identity checks; that removes duplicated lifecycle logic and makes the inferred type reflect the states the parser accepts. _Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1hcHByb2FjaC1sdW5hLTEiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzI5OTAiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiNTYwMWM5NmEtZTgxYS00NTgxLTllYjktYzE0NWIyYWI4MTQxIn0= -->
@ -154,0 +155,4 @@
job: JobRecord,
analysis: Analysis.nullish().transform((value) => value ?? undefined),
})
.superRefine(validateQuerySubmission);

Model the lifecycle in the schema instead of accepting optional analysis_id, error, and analysis and then rejecting their invalid combinations in superRefine. Define JobRecord with status-discriminated variants and compose pending/running, failed, and completed envelope variants, keeping a small refinement only for cross-record ID/question equality. superRefine can reject bad payloads at runtime, but the inferred QuerySubmission remains a bag of optionals, so consumers can still represent and must defensively handle states the parser promises are impossible; the separate validator also duplicates the schema as SubmissionForValidation.

Model the lifecycle in the schema instead of accepting optional `analysis_id`, `error`, and `analysis` and then rejecting their invalid combinations in `superRefine`. Define `JobRecord` with status-discriminated variants and compose pending/running, failed, and completed envelope variants, keeping a small refinement only for cross-record ID/question equality. `superRefine` can reject bad payloads at runtime, but the inferred `QuerySubmission` remains a bag of optionals, so consumers can still represent and must defensively handle states the parser promises are impossible; the separate validator also duplicates the schema as `SubmissionForValidation`.
Author
Owner

Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.

<!-- gh-feedback:reply-to:69875 --> Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.
jercik marked this conversation as resolved
forgejo-actions left a comment

Approach review: The exit-code and rendering changes are direct, but the response lifecycle should be represented as schema variants rather than a parallel procedural state machine; see the inline alternative.

Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Approach review:** The exit-code and rendering changes are direct, but the response lifecycle should be represented as schema variants rather than a parallel procedural state machine; see the inline alternative. _Approach review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1hcHByb2FjaC1zbWFydC0xIiwic291cmNlV29ya2Zsb3dSdW5JZCI6IjMyOTkwIiwic291cmNlR2VuZXJhdG9yQXR0ZW1wdCI6IjEiLCJyZXN1bHRJZCI6IjljZGZhNzA0LTdlZGUtNGY3ZC05MDM3LWUzMmI5ODQ4MDg4MSJ9 -->
@ -154,0 +155,4 @@
job: JobRecord,
analysis: Analysis.nullish().transform((value) => value ?? undefined),
})
.superRefine(validateQuerySubmission);

The lifecycle rules should be part of the parsed type instead of a parallel void validator. Define JobRecord as a z.discriminatedUnion("status", ...) with analysis_id required only for completed, error required only for failed, and both forbidden for pending/running; then compose submission variants so analysis is required only for completed. Keep superRefine only for cross-record ID equality. This removes most of validate-query-submission.ts and makes z.infer<typeof QuerySubmission> exclude the impossible states currently admitted by the optional fields.

The lifecycle rules should be part of the parsed type instead of a parallel `void` validator. Define `JobRecord` as a `z.discriminatedUnion("status", ...)` with `analysis_id` required only for `completed`, `error` required only for `failed`, and both forbidden for `pending`/`running`; then compose submission variants so `analysis` is required only for `completed`. Keep `superRefine` only for cross-record ID equality. This removes most of `validate-query-submission.ts` and makes `z.infer<typeof QuerySubmission>` exclude the impossible states currently admitted by the optional fields.
Author
Owner

Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.

<!-- gh-feedback:reply-to:69882 --> Fixed in 5654c88: lifecycle variants encode permitted fields; refinement now checks only cross-record links.
jercik marked this conversation as resolved
forgejo-actions left a comment

Summary: Found 1 medium issue in the new poll-command output.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** Found 1 medium issue in the new poll-command output. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzI5OTAiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiYTQyYTkwNTYtMjI3MC00NDRjLWFjMzUtN2NlMzgwMjZlMzAwIn0= -->
@ -31,0 +41,4 @@
},
{
stream: "stdout",
text: `tropkod-client --url ${shellQuote(options.serviceUrl.href)} --job ${shellQuote(submission.job.id)}`,

🟡 Medium: This command always uses POSIX single-quote escaping, but the package has no POSIX-only platform constraint. For example, the newly accepted job's; still safe ID is rendered as --job 'job'\''s; still safe', which PowerShell cannot parse because an apostrophe inside its single-quoted strings must be doubled. A Windows user therefore cannot copy the advertised poll command for a valid response. Emit shell-specific commands/escaping, or print the argument values separately when the target shell is unknown.

🟡 **Medium:** This command always uses POSIX single-quote escaping, but the package has no POSIX-only platform constraint. For example, the newly accepted `job's; still safe` ID is rendered as `--job 'job'\''s; still safe'`, which PowerShell cannot parse because an apostrophe inside its single-quoted strings must be doubled. A Windows user therefore cannot copy the advertised poll command for a valid response. Emit shell-specific commands/escaping, or print the argument values separately when the target shell is unknown.
Author
Owner

Fixed in 5654c88: output is labeled JSON argv data, not shell syntax, and is covered on hostile identifiers.

<!-- gh-feedback:reply-to:69892 --> Fixed in 5654c88: output is labeled JSON argv data, not shell syntax, and is covered on hostile identifiers.
jercik marked this conversation as resolved
forgejo-actions left a comment

Summary: Reviewed the exit-code change (2 → 0 for pending/running), the new cross-field response validation, and the poll-command hint. Found 2 medium and 2 low issues.

What I verified locally (Node 26.5, zod 4.4.3, a stub tropkod server): all 22 cases in src/query-submission.test.ts pass against real zod; the five src/cli.integration.test.ts scenarios reproduce exactly (exit 0 for running/completed, exit 1 for failed, the set -euo pipefail pipeline exits 0 with empty stderr, and the printed poll command matches the expected shell-quoted text without leaking the API key); the new README bash example runs and prints poll the returned job id later; shellQuote correctly renders job's; still safe as 'job'\''s; still safe'; and no stale exit 2 references remain in the repo.

The two medium findings are both cases where the new superRefine converts a usable server response into exit 6 (documented as terminal schema drift) — I confirmed the failed-job-without-error case end to end against a stub server.

Code review by Claude Code · Personal 01 (opus)

**Summary:** Reviewed the exit-code change (2 → 0 for pending/running), the new cross-field response validation, and the poll-command hint. Found 2 medium and 2 low issues. What I verified locally (Node 26.5, zod 4.4.3, a stub tropkod server): all 22 cases in `src/query-submission.test.ts` pass against real zod; the five `src/cli.integration.test.ts` scenarios reproduce exactly (exit `0` for running/completed, exit `1` for failed, the `set -euo pipefail` pipeline exits `0` with empty stderr, and the printed poll command matches the expected shell-quoted text without leaking the API key); the new README bash example runs and prints `poll the returned job id later`; `shellQuote` correctly renders `job's; still safe` as `'job'\''s; still safe'`; and no stale `exit 2` references remain in the repo. The two medium findings are both cases where the new `superRefine` converts a usable server response into exit `6` (documented as terminal schema drift) — I confirmed the failed-job-without-`error` case end to end against a stub server. _Code review by Claude Code · Personal 01 (opus)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLXNtYXJ0LTEiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzI5OTAiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiYTIwZjY2ZjItYjc4Yi00Yjk4LTk3ZjgtOTVjZDZlYTQ1MGZlIn0= -->
@ -0,0 +6,4 @@
.refine((value) => value === value.trim(), {
message: "job.id must not have outer whitespace",
})
.refine((value) => value !== "." && value !== ".." && !value.includes("/"), {

🟢 Low: These two rules duplicate the --job input validation in src/resolve-submission-request.ts (jobId === "." || jobId === ".." || jobId.includes("/"), plus the emptiness check), which already carries the comment explaining why: encodeURIComponent leaves dots unescaped. Two copies of a path-segment guard can drift, and only one of them is covered by the new tests.

Reusing JobId in resolveSubmissionRequest (parse the trimmed value and map a failure to the existing usage CliError) keeps the request-side and response-side definitions of "a single path segment" identical.

🟢 **Low:** These two rules duplicate the `--job` input validation in `src/resolve-submission-request.ts` (`jobId === "." || jobId === ".." || jobId.includes("/")`, plus the emptiness check), which already carries the comment explaining why: `encodeURIComponent` leaves dots unescaped. Two copies of a path-segment guard can drift, and only one of them is covered by the new tests. Reusing `JobId` in `resolveSubmissionRequest` (parse the trimmed value and map a failure to the existing `usage` `CliError`) keeps the request-side and response-side definitions of "a single path segment" identical.
Author
Owner

Fixed in 5654c88: response recovery and --job parsing now reuse the shared hardened JobId schema.

<!-- gh-feedback:reply-to:69897 --> Fixed in 5654c88: response recovery and --job parsing now reuse the shared hardened JobId schema.
jercik marked this conversation as resolved
@ -154,0 +155,4 @@
job: JobRecord,
analysis: Analysis.nullish().transform((value) => value ?? undefined),
})
.superRefine(validateQuerySubmission);

🟢 Low: The refinement makes the present/absent distinction on job.error and job.analysis_id load-bearing, but both are declared .optional(), so an explicit null — the normal JSON serialization of an absent nullable column — fails the object parse before the refinement runs.

Confirmed with zod 4.4.3: a pending job carrying "analysis_id": null, "error": null is rejected with expected string, received null on both fields (exit 6), even though that payload means exactly what forbidAnalysisArtifacts wants to allow.

AGENTS.md's own rule prescribes .nullish() for backend fields that may be absent. .nullish().transform((value) => value ?? undefined) on both — matching how analysis is already declared — would collapse null and missing into the undefined the refinement checks for.

🟢 **Low:** The refinement makes the present/absent distinction on `job.error` and `job.analysis_id` load-bearing, but both are declared `.optional()`, so an explicit `null` — the normal JSON serialization of an absent nullable column — fails the object parse before the refinement runs. Confirmed with zod 4.4.3: a `pending` job carrying `"analysis_id": null, "error": null` is rejected with `expected string, received null` on both fields (exit `6`), even though that payload means exactly what `forbidAnalysisArtifacts` wants to allow. AGENTS.md's own rule prescribes `.nullish()` for backend fields that may be absent. `.nullish().transform((value) => value ?? undefined)` on both — matching how `analysis` is already declared — would collapse `null` and missing into the `undefined` the refinement checks for.
Author
Owner

Fixed in 5654c88: the transport boundary normalizes nullable lifecycle fields to absence before canonical parsing.

<!-- gh-feedback:reply-to:69896 --> Fixed in 5654c88: the transport boundary normalizes nullable lifecycle fields to absence before canonical parsing.
jercik marked this conversation as resolved
@ -0,0 +46,4 @@
if (submission.job.status === "failed") {
forbidAnalysisArtifacts(submission, context, false);
if (submission.job.error === undefined) {
addIssue(context, "failed jobs require job.error", ["job", "error"]);

🟡 Medium: Requiring job.error on a failed job turns the server's own terminal verdict into exit 6.

Verified against a stub server: a failed job whose body omits error now yields

{"error":{"kind":"invalid-response","message":"tropkod response did not match the expected schema (HTTP 200, job job-9)","status":200,"job_id":"job-9","exit_code":6}}

Before this change the same body exited 1 and printed Job job-9 is failed. The README defines exit 1 as "the server's own verdict" and tells poll loops to end at "a completed or failed job envelope"; a repeated exit 6 is documented as terminal schema drift to report rather than poll. So a worker that dies without recording a message (OOM kill, hard timeout) makes the CLI report a client/server contract bug instead of the failure that actually happened.

The same body shape with "error": null fails even earlier, at JobRecord.error's .optional() — also exit 6.

Safer: keep failed renderable and drop this issue, letting formatSubmission fall back (it already guards job.error !== undefined) — the status alone is the verdict, and the message is only decoration.

🟡 **Medium:** Requiring `job.error` on a failed job turns the server's own terminal verdict into exit `6`. Verified against a stub server: a `failed` job whose body omits `error` now yields ```json {"error":{"kind":"invalid-response","message":"tropkod response did not match the expected schema (HTTP 200, job job-9)","status":200,"job_id":"job-9","exit_code":6}} ``` Before this change the same body exited `1` and printed `Job job-9 is failed.` The README defines exit `1` as "the server's own verdict" and tells poll loops to end at "a completed or failed job envelope"; a repeated exit `6` is documented as terminal schema drift to report rather than poll. So a worker that dies without recording a message (OOM kill, hard timeout) makes the CLI report a client/server contract bug instead of the failure that actually happened. The same body shape with `"error": null` fails even earlier, at `JobRecord.error`'s `.optional()` — also exit `6`. Safer: keep `failed` renderable and drop this issue, letting `formatSubmission` fall back (it already guards `job.error !== undefined`) — the status alone is the verdict, and the message is only decoration.
Author
Owner

Declining this change: the producer database contract requires every failed job to carry a nonempty error, and the service schema now enforces the same invariant.

<!-- gh-feedback:reply-to:69894 --> Declining this change: the producer database contract requires every failed job to carry a nonempty error, and the service schema now enforces the same invariant.
@ -0,0 +73,4 @@
addIssue(context, "analysis.session_id must match session.id", ["analysis", "session_id"]);
}
if (submission.analysis.question !== submission.query.question) {
addIssue(context, "analysis.question must match query.question", ["analysis", "question"]);

🟡 Medium: Byte-exact analysis.question vs query.question is the most fragile invariant here, and failing it discards a completed, already-paid analysis.

Confirmed: a completed envelope differing only by one trailing space between the two fields is rejected with analysis.question: analysis.question must match query.question, so the whole envelope becomes exit 6 and the answer is unrenderable in both human and --json mode. Any future server-side normalization (trim, whitespace collapse, unicode normalization) of one field and not the other silently breaks every completed job for this client build.

The linkage this check is after is already fully established by the surrounding id checks (analysis.query_id === query.id, analysis.session_id === session.id, analysis.provenance.job_id === job.id), which cannot drift on formatting. Consider dropping the question comparison, or comparing trimmed values.

🟡 **Medium:** Byte-exact `analysis.question` vs `query.question` is the most fragile invariant here, and failing it discards a completed, already-paid analysis. Confirmed: a completed envelope differing only by one trailing space between the two fields is rejected with `analysis.question: analysis.question must match query.question`, so the whole envelope becomes exit `6` and the answer is unrenderable in both human and `--json` mode. Any future server-side normalization (trim, whitespace collapse, unicode normalization) of one field and not the other silently breaks every completed job for this client build. The linkage this check is after is already fully established by the surrounding id checks (`analysis.query_id === query.id`, `analysis.session_id === session.id`, `analysis.provenance.job_id === job.id`), which cannot drift on formatting. Consider dropping the question comparison, or comparing trimmed values.
Author
Owner

Declining this change: the producer derives both question fields from the same persisted query value, so exact equality is an intentional contract-drift check.

<!-- gh-feedback:reply-to:69895 --> Declining this change: the producer derives both question fields from the same persisted query value, so exact equality is an intentional contract-drift check.
forgejo-actions left a comment

Summary: Found 1 critical issue: the generated poll command can become executable PowerShell input when a server-controlled job id contains shell metacharacters.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** Found 1 critical issue: the generated poll command can become executable PowerShell input when a server-controlled job id contains shell metacharacters. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEtMiIsInNvdXJjZVdvcmtmbG93UnVuSWQiOiIzMjk5MCIsInNvdXJjZUdlbmVyYXRvckF0dGVtcHQiOiIxIiwicmVzdWx0SWQiOiJmOWNlNmM1Ni01YTBkLTRhYTEtYTIwMy04YTIxNWQ1MmMxNzIifQ== -->
@ -55,2 +69,4 @@
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", String.raw`'\''`)}'`;

🔴 Critical: This emits POSIX-only quoting for a command presented as ready to paste, but the npm CLI has no Windows restriction and the new JobId parser explicitly accepts apostrophes and semicolons. In PowerShell, a value such as x'; Write-Output pwned; # makes the generated --job 'x'\''; ... leave the semicolon outside a quoted string (PowerShell escapes an apostrophe as '', not \''), so pasting output from an untrusted service can execute attacker-controlled commands. Avoid emitting an executable command from opaque server data, or constrain every interpolated value to a shell-portable safe alphabet and cover the supported Windows shells as well as Bash.

🔴 **Critical:** This emits POSIX-only quoting for a command presented as ready to paste, but the npm CLI has no Windows restriction and the new `JobId` parser explicitly accepts apostrophes and semicolons. In PowerShell, a value such as `x'; Write-Output pwned; #` makes the generated `--job 'x'\''; ...` leave the semicolon outside a quoted string (PowerShell escapes an apostrophe as `''`, not `\''`), so pasting output from an untrusted service can execute attacker-controlled commands. Avoid emitting an executable command from opaque server data, or constrain every interpolated value to a shell-portable safe alphabet and cover the supported Windows shells as well as Bash.
Author
Owner

Fixed in 5654c88: opaque values are emitted as labeled JSON argv data rather than executable shell syntax.

<!-- gh-feedback:reply-to:69899 --> Fixed in 5654c88: opaque values are emitted as labeled JSON argv data rather than executable shell syntax.
jercik marked this conversation as resolved
forgejo-actions left a comment

Summary: Found 1 medium issue.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** Found 1 medium issue. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiOWEwOTcxMDViOWMyMWI1YjdiOGQyNGY5NDRhYmY5M2RmNzY3MjQxZSIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEtMyIsInNvdXJjZVdvcmtmbG93UnVuSWQiOiIzMjk5MCIsInNvdXJjZUdlbmVyYXRvckF0dGVtcHQiOiIxIiwicmVzdWx0SWQiOiI1MGZhY2IwMC1kNDEzLTQwZDktOGM3OS1iMTUzN2NkOGFmNGEifQ== -->
@ -55,2 +69,4 @@
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", String.raw`'\''`)}'`;

🟡 Medium: This escaping only produces a POSIX-shell command, although the package has no OS restriction. In cmd.exe, single quotes are passed literally, so even an ordinary generated --url '…' value is invalid; in PowerShell, an embedded apostrophe must be doubled rather than escaped as '\'', so the hostile-ID case is not copyable there either. Render quoting for the current platform (or provide explicit POSIX and PowerShell/cmd variants) so the advertised poll command works for Windows users.

🟡 **Medium:** This escaping only produces a POSIX-shell command, although the package has no OS restriction. In `cmd.exe`, single quotes are passed literally, so even an ordinary generated `--url '…'` value is invalid; in PowerShell, an embedded apostrophe must be doubled rather than escaped as `'\''`, so the hostile-ID case is not copyable there either. Render quoting for the current platform (or provide explicit POSIX and PowerShell/cmd variants) so the advertised poll command works for Windows users.
Author
Owner

Fixed in 5654c88: the platform-neutral output is JSON argv data rather than shell-specific quoting.

<!-- gh-feedback:reply-to:69903 --> Fixed in 5654c88: the platform-neutral output is JSON argv data rather than shell-specific quoting.
jercik marked this conversation as resolved
fix: harden asynchronous job protocol
All checks were successful
PR Review / Prepare immutable review tools (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-smart-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-2 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-3 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-smart-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna-2 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna-3 generator (pull_request_target) Has been skipped
PR Review / Dispatch and observe exact review writers (pull_request_target) Has been skipped
PR Review / Request trusted main review (pull_request_target) Successful in 3s
commit-msg / commitlint (pull_request) Successful in 23s
Checks / quality-checks (26.5.0) (pull_request) Successful in 1m0s
Checks / quality-checks (24.15.0) (pull_request) Successful in 1m1s
5654c88aa3
Author
Owner

Replying to review #16514

Implemented lifecycle variants and cross-record-only refinement in 5654c88.

> Replying to review #16514 Implemented lifecycle variants and cross-record-only refinement in 5654c88.
Author
Owner

Replying to review #16516

Implemented lifecycle variants and platform-neutral JSON poll data in 5654c88.

> Replying to review #16516 Implemented lifecycle variants and platform-neutral JSON poll data in 5654c88.
Author
Owner

Replying to review #16518

Implemented lifecycle variants and cross-record-only refinement in 5654c88.

> Replying to review #16518 Implemented lifecycle variants and cross-record-only refinement in 5654c88.
Author
Owner

Replying to review #16523

Implemented lifecycle variants and cross-record-only refinement in 5654c88.

> Replying to review #16523 Implemented lifecycle variants and cross-record-only refinement in 5654c88.
Author
Owner

Replying to review #16529

Fixed the poll-output finding in 5654c88 by emitting labeled JSON argv data.

> Replying to review #16529 Fixed the poll-output finding in 5654c88 by emitting labeled JSON argv data.
Author
Owner

Replying to review #16530

Processed all four inline findings: null normalization and shared JobId parsing were fixed in 5654c88; the two producer-backed invariants were retained with evidence in their threads.

> Replying to review #16530 Processed all four inline findings: null normalization and shared JobId parsing were fixed in 5654c88; the two producer-backed invariants were retained with evidence in their threads.
Author
Owner

Replying to review #16531

Fixed the shell-injection finding in 5654c88 by removing executable shell syntax from the output.

> Replying to review #16531 Fixed the shell-injection finding in 5654c88 by removing executable shell syntax from the output.
Author
Owner

Replying to review #16533

Fixed the portability finding in 5654c88 with platform-neutral JSON argv data.

> Replying to review #16533 Fixed the portability finding in 5654c88 with platform-neutral JSON argv data.
forgejo-actions left a comment

Summary: No actionable issues found.

Code review by Codex CLI · Personal 02 (gpt-5.6-luna)

**Summary:** No actionable issues found. _Code review by Codex CLI · Personal 02 (gpt-5.6-luna)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiNTY1NGM4OGFhM2YyODI5Y2QyYTU4YmFjMDg3NDYxODJlODA1ZGNkNiIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEtMyIsInNvdXJjZVdvcmtmbG93UnVuSWQiOiIzMzA4NSIsInNvdXJjZUdlbmVyYXRvckF0dGVtcHQiOiIxIiwicmVzdWx0SWQiOiIzNzg3MzgzNi00MjgyLTQwZGQtYjQ5MS1lZmIxMTVlODFiZjgifQ== -->
forgejo-actions left a comment

Summary: Found 1 medium issue.

Code review by Codex CLI · Personal 02 (gpt-5.6-luna)

**Summary:** Found 1 medium issue. _Code review by Codex CLI · Personal 02 (gpt-5.6-luna)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiNTY1NGM4OGFhM2YyODI5Y2QyYTU4YmFjMDg3NDYxODJlODA1ZGNkNiIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzMwODUiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiMTQwOGJlNTMtNWZiZS00MmJjLWJhMjctN2UzZjA0MTBmODcwIn0= -->
@ -0,0 +10,4 @@
.refine((value) => !value.includes("\0"), {
message: "job.id must not contain NUL",
})
.refine((value) => value.isWellFormed(), {

🟡 Medium: isWellFormed() rejects lone surrogates but still accepts U+2028/U+2029. JSON.stringify() emits those Unicode line separators literally, so a remote job ID can break the promised one-line human poll/diagnostic output and confuse line-oriented consumers. Reject or escape line-separator/control characters before using JobId in rendered output.

🟡 **Medium:** `isWellFormed()` rejects lone surrogates but still accepts U+2028/U+2029. `JSON.stringify()` emits those Unicode line separators literally, so a remote job ID can break the promised one-line human poll/diagnostic output and confuse line-oriented consumers. Reject or escape line-separator/control characters before using `JobId` in rendered output.
Author
Owner

Fixed in ae77d6c: all JSON-literal human values escape Unicode line separators while preserving round-trip data.

<!-- gh-feedback:reply-to:70160 --> Fixed in ae77d6c: all JSON-literal human values escape Unicode line separators while preserving round-trip data.
jercik marked this conversation as resolved
forgejo-actions left a comment

Summary: No actionable issues found.

Code review by Codex CLI · Personal 02 (gpt-5.6-luna)

**Summary:** No actionable issues found. _Code review by Codex CLI · Personal 02 (gpt-5.6-luna)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiNTY1NGM4OGFhM2YyODI5Y2QyYTU4YmFjMDg3NDYxODJlODA1ZGNkNiIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEtMiIsInNvdXJjZVdvcmtmbG93UnVuSWQiOiIzMzA4NSIsInNvdXJjZUdlbmVyYXRvckF0dGVtcHQiOiIxIiwicmVzdWx0SWQiOiI2ZGU0NTU2Zi0zM2UzLTRhNzEtYjkwMy05YmIxOTg5MWVmNGIifQ== -->
forgejo-actions left a comment

Summary: Found 1 medium issue.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** Found 1 medium issue. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiNTY1NGM4OGFhM2YyODI5Y2QyYTU4YmFjMDg3NDYxODJlODA1ZGNkNiIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLXNtYXJ0LTEiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzMwODUiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiZDYzMDYxYzEtY2NiMy00NmQ5LWE4MTItYjQyMjBkZDAwODczIn0= -->
@ -0,0 +22,4 @@
context: z.RefinementCtx,
): void {
validateQueryLinks(submission, context);
validateAnalysisLinks(submission, context);

🟡 Medium: This validator checks record IDs but leaves the nested target indexes unchecked. I verified that an answered envelope with one resolved/source target and both source_alignments[0].target_index and grounding.spans[0].target_index set to 99 still parses and is rendered with exit 0. That accepts a contradictory snapshot and can present evidence attributed to no real target. Validate each index against the corresponding target array before accepting the envelope.

🟡 **Medium:** This validator checks record IDs but leaves the nested target indexes unchecked. I verified that an answered envelope with one resolved/source target and both `source_alignments[0].target_index` and `grounding.spans[0].target_index` set to `99` still parses and is rendered with exit `0`. That accepts a contradictory snapshot and can present evidence attributed to no real target. Validate each index against the corresponding target array before accepting the envelope.
Author
Owner

Fixed in ae77d6c: source alignments and evidence spans must reference an existing source target.

<!-- gh-feedback:reply-to:70167 --> Fixed in ae77d6c: source alignments and evidence spans must reference an existing source target.
jercik marked this conversation as resolved
fix: validate rendered job evidence
All checks were successful
PR Review / Prepare immutable review tools (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-smart-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-2 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-approach-luna-3 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-smart-1 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna-2 generator (pull_request_target) Has been skipped
PR Review / forgejo-review-code-luna-3 generator (pull_request_target) Has been skipped
PR Review / Dispatch and observe exact review writers (pull_request_target) Has been skipped
PR Review / Request trusted main review (pull_request_target) Successful in 3s
commit-msg / commitlint (pull_request) Successful in 18s
Checks / quality-checks (26.5.0) (pull_request) Successful in 32s
Checks / quality-checks (24.15.0) (pull_request) Successful in 35s
ae77d6c3de
Author
Owner

Replying to review #16564

Acknowledged; no actionable finding was reported.

> Replying to review #16564 Acknowledged; no actionable finding was reported.
Author
Owner

Replying to review #16566

Fixed the Unicode line-separator finding in ae77d6c.

> Replying to review #16566 Fixed the Unicode line-separator finding in ae77d6c.
Author
Owner

Replying to review #16568

Acknowledged; no actionable finding was reported.

> Replying to review #16568 Acknowledged; no actionable finding was reported.
Author
Owner

Replying to review #16569

Fixed the source-target linkage finding in ae77d6c.

> Replying to review #16569 Fixed the source-target linkage finding in ae77d6c.
forgejo-actions left a comment

Summary: No actionable issues found.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** No actionable issues found. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiYWU3N2Q2YzNkZWRjMmE2NjhmNjkxZjdkOWM5MTdjZTgyYzY4MGZjOCIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEtMiIsInNvdXJjZVdvcmtmbG93UnVuSWQiOiIzMzEzMSIsInNvdXJjZUdlbmVyYXRvckF0dGVtcHQiOiIxIiwicmVzdWx0SWQiOiIyODcyZmFhMS0yMThhLTQ0ODItYTc0OC0yMzIyMjk4YmM0MmEifQ== -->
forgejo-actions left a comment

Summary: No actionable issues found.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** No actionable issues found. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiYWU3N2Q2YzNkZWRjMmE2NjhmNjkxZjdkOWM5MTdjZTgyYzY4MGZjOCIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLXNtYXJ0LTEiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzMxMzEiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiNDA5NjIyMzEtYTIwOC00MGY2LWFlOGYtODdjNDNlODAxZGQyIn0= -->
forgejo-actions left a comment

Summary: No actionable issues found.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** No actionable issues found. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiYWU3N2Q2YzNkZWRjMmE2NjhmNjkxZjdkOWM5MTdjZTgyYzY4MGZjOCIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEiLCJzb3VyY2VXb3JrZmxvd1J1bklkIjoiMzMxMzEiLCJzb3VyY2VHZW5lcmF0b3JBdHRlbXB0IjoiMSIsInJlc3VsdElkIjoiOTk0NTBjZTktZWY2NC00NmU3LTg5NzAtMGZkODUwMGM2NTczIn0= -->
forgejo-actions left a comment

Summary: No actionable issues found.

Code review by Codex CLI · Personal 01 (gpt-5.6-sol)

**Summary:** No actionable issues found. _Code review by Codex CLI · Personal 01 (gpt-5.6-sol)_ <!-- axrecipe-review:v2:eyJzY2hlbWFWZXJzaW9uIjoyLCJzdGF0ZSI6InB1Ymxpc2hlZCIsInJlcG9zaXRvcnkiOiJqNGstb3NzL3Ryb3Brb2QtY2xpZW50IiwibnVtYmVyIjoiMTMiLCJiYXNlUmVmIjoibWFpbiIsIm1lcmdlQmFzZVNoYSI6IjU0ZjdjYjM4NjRiODI3ODkyZjgwNjEyODI2MTc3NTQyZTI3MDY0MDAiLCJoZWFkU2hhIjoiYWU3N2Q2YzNkZWRjMmE2NjhmNjkxZjdkOWM5MTdjZTgyYzY4MGZjOCIsInNsb3QiOiJmb3JnZWpvLXJldmlldy1jb2RlLWx1bmEtMyIsInNvdXJjZVdvcmtmbG93UnVuSWQiOiIzMzEzMSIsInNvdXJjZUdlbmVyYXRvckF0dGVtcHQiOiIxIiwicmVzdWx0SWQiOiJjYmUwMjc1OC0yZjM0LTQ4ZjctYTBmNS1jOGZjMGM0MDJiMGIifQ== -->
Author
Owner

Replying to review #16590

Acknowledged; no actionable finding was reported.

> Replying to review #16590 Acknowledged; no actionable finding was reported.
Author
Owner

Replying to review #16591

Acknowledged; no actionable finding was reported.

> Replying to review #16591 Acknowledged; no actionable finding was reported.
Author
Owner

Replying to review #16593

Acknowledged; no actionable finding was reported.

> Replying to review #16593 Acknowledged; no actionable finding was reported.
Author
Owner

Replying to review #16595

Acknowledged; no actionable finding was reported.

> Replying to review #16595 Acknowledged; no actionable finding was reported.
jercik merged commit b005613113 into main 2026-08-26 13:39:11 +00:00
jercik deleted branch feat/nonfailed-job-status 2026-08-26 13:39:11 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
j4k-oss/tropkod-client!13
No description provided.