Value: Shell scripts need to distinguish a finished response from a useful-looking fragment before moving to the next command. This matters for generated configuration, migrations, structured text, and code-only output where silent truncation can invalidate the artifact. An opt-in completion requirement gives automation a reliable branch without disrupting today's interactive-friendly partial output.
Problem today: Sources/CLI.swift:75 prints the result and then returns zero at :86; the length outcome becomes only a stderr warning at :114. Sources/Models.swift:10 has no finish reason in ApfelResponse, so --json consumers cannot branch on it. Sources/Core/Chat/StreamOutcome.swift:45 already represents partial output as a length finish. This proposal preserves #128/#130's existing default and is separate from #435's exhausted tool-loop error.
Proposal: Add --require-complete for single-prompt, streaming, --messages, and --code workflows. When generation ends with .length, return a new documented CLI exit code, proposed 8. For non-streaming output, check completion before writing stdout; for streaming, retain already emitted text but return the nonzero status. A .stop result follows the current output path, and existing model/tool/refusal errors retain their established classifications.
Add an optional/additive finish_reason field to the normal JSON envelope wherever an engine outcome is available. Do not infer completion from closing fences, valid JSON, or the absence of an exception. This shell workflow should only print the generated artifact when apfel reports completion:
if result=$(apfel --require-complete --code --max-tokens 128 'Write a small Python CSV reader.'); then
printf '%s\n' "$result"
fi
Acceptance criteria:
- First add failing output-policy tests for
.stop, explicit cap exhaustion, and output-side context exhaustion using injected outcomes.
- Non-streaming
--require-complete plus a length finish produces no stdout and exits 8; streaming may have partial stdout but must exit 8.
- Plain output, JSON envelopes,
--code, and --messages agree on status; quiet mode cannot suppress a failure exit.
- JSON reports
finish_reason without changing the meaning or encoding of content; unsupported flag combinations produce specific CLI parse errors.
- Preserve existing default partial-output behavior and add a live deliberately capped request test alongside deterministic policy coverage.
- Document
set -o pipefail for callers that place apfel inside a pipeline, and document that completion is not a correctness guarantee.
Golden-goal alignment: Gives the UNIX tool an explicit success contract for scripts that require a complete generated artifact.
Effort: M
Risks / open questions:
- Reserve exit 8 through the existing exit-code catalogue and stability policy; never reuse an established meaning.
- The framework can stop naturally after a semantically incomplete answer; this flag reports engine termination, not factual or syntactic validation.
- A streaming command cannot retract bytes already consumed downstream; the nonzero exit is the contract.
Value: Shell scripts need to distinguish a finished response from a useful-looking fragment before moving to the next command. This matters for generated configuration, migrations, structured text, and code-only output where silent truncation can invalidate the artifact. An opt-in completion requirement gives automation a reliable branch without disrupting today's interactive-friendly partial output.
Problem today: Sources/CLI.swift:75 prints the result and then returns zero at
:86; the length outcome becomes only a stderr warning at:114. Sources/Models.swift:10 has no finish reason inApfelResponse, so--jsonconsumers cannot branch on it.Sources/Core/Chat/StreamOutcome.swift:45already represents partial output as a length finish. This proposal preserves #128/#130's existing default and is separate from #435's exhausted tool-loop error.Proposal: Add
--require-completefor single-prompt, streaming,--messages, and--codeworkflows. When generation ends with.length, return a new documented CLI exit code, proposed 8. For non-streaming output, check completion before writing stdout; for streaming, retain already emitted text but return the nonzero status. A.stopresult follows the current output path, and existing model/tool/refusal errors retain their established classifications.Add an optional/additive
finish_reasonfield to the normal JSON envelope wherever an engine outcome is available. Do not infer completion from closing fences, valid JSON, or the absence of an exception. This shell workflow should only print the generated artifact when apfel reports completion:Acceptance criteria:
.stop, explicit cap exhaustion, and output-side context exhaustion using injected outcomes.--require-completeplus a length finish produces no stdout and exits 8; streaming may have partial stdout but must exit 8.--code, and--messagesagree on status; quiet mode cannot suppress a failure exit.finish_reasonwithout changing the meaning or encoding ofcontent; unsupported flag combinations produce specific CLI parse errors.set -o pipefailfor callers that place apfel inside a pipeline, and document that completion is not a correctness guarantee.Golden-goal alignment: Gives the UNIX tool an explicit success contract for scripts that require a complete generated artifact.
Effort: M
Risks / open questions: