polyphony CLI exits 0 on unrecognized args (silent-failure root cause)
Summary
The polyphony CLI prints a usage message and exits 0 when invoked
with an unknown verb or unrecognized argument. Combined with workflow
agents that route on output.error (routing-style envelope), this
makes signature drift in workflow YAMLs invisible to conductor —
every step shows ✓, but no JSON is emitted and downstream agents read
Undefined for what they expected to be output.waves,
output.has_conflicts, etc.
Concrete cases observed
PR #159 caught three signature drifts this way:
polyphony branch ensure-feature was being passed --apex-id
instead of --root-id.
polyphony worklist build was being called positionally instead of
with --root-id.
wave-integrator.ps1 invoked polyphony edges check with the
wrong arg shape.
Conductor reported "✓" on all three. The pipeline wasn't actually
making forward progress; downstream agents silently received
Undefined payloads.
Root cause
ConsoleAppFramework's default behavior: when a verb / option doesn't
parse, the framework prints help text to stderr and exits 0 (treating
it as a help request, not an error).
Proposal
Distinguish two failure classes:
- Parser / unknown-arg failures → exit non-zero with a
diagnostic on stderr. These are not routing-style; they indicate
the CLI was invoked wrong.
- Domain failures inside a known verb (e.g.
root declare on a
non-existent work item) → continue to use routing-style: exit 0
(or 1 today; design TBD) with a JSON envelope carrying
error / error_code.
The distinction matters because conductor's routing-style when:
clauses can't recover from class (1) — there's no envelope to read.
Implementation sketch
Wrap the ConsoleAppFramework App.RunAsync in an exception handler
that catches ArgumentException / parser-mismatch errors and exits
with a non-zero code distinct from domain-error exit codes. Add a
diagnostic to stderr that includes the actual command line received.
Add a contract test that invokes each registered verb with an
intentionally-wrong arg and asserts a non-zero exit code.
Why this wasn't fixed in PR #159
PR #159 surfaced the bug class but addressed it locally — by adding a
21-test signature-contract Pester suite (verb-signature-contracts.Tests.ps1)
that asserts every CLI call site in apex YAMLs matches the verb's
documented signature. That's a structural mitigation; the underlying
CLI behavior remains as-is. Fixing it properly removes a whole class
of future drift (the contract suite catches today's mistakes; the CLI
fix prevents tomorrow's).
Acceptance
- Polyphony CLI exits non-zero on unknown verb / unrecognized option.
- Distinct exit code for parser failures vs domain (routing-style)
failures.
- Contract test pinning the new behavior across all registered verbs.
- Doc update in
docs/polyphony-cli-reference.md.
Lineage
polyphonyCLI exits 0 on unrecognized args (silent-failure root cause)Summary
The polyphony CLI prints a usage message and exits 0 when invoked
with an unknown verb or unrecognized argument. Combined with workflow
agents that route on
output.error(routing-style envelope), thismakes signature drift in workflow YAMLs invisible to conductor —
every step shows ✓, but no JSON is emitted and downstream agents read
Undefinedfor what they expected to beoutput.waves,output.has_conflicts, etc.Concrete cases observed
PR #159 caught three signature drifts this way:
polyphony branch ensure-featurewas being passed--apex-idinstead of
--root-id.polyphony worklist buildwas being called positionally instead ofwith
--root-id.wave-integrator.ps1invokedpolyphony edges checkwith thewrong arg shape.
Conductor reported "✓" on all three. The pipeline wasn't actually
making forward progress; downstream agents silently received
Undefinedpayloads.Root cause
ConsoleAppFramework's default behavior: when a verb / option doesn'tparse, the framework prints help text to stderr and exits 0 (treating
it as a help request, not an error).
Proposal
Distinguish two failure classes:
diagnostic on stderr. These are not routing-style; they indicate
the CLI was invoked wrong.
root declareon anon-existent work item) → continue to use routing-style: exit 0
(or 1 today; design TBD) with a JSON envelope carrying
error/error_code.The distinction matters because conductor's routing-style
when:clauses can't recover from class (1) — there's no envelope to read.
Implementation sketch
Wrap the ConsoleAppFramework
App.RunAsyncin an exception handlerthat catches
ArgumentException/ parser-mismatch errors and exitswith a non-zero code distinct from domain-error exit codes. Add a
diagnostic to stderr that includes the actual command line received.
Add a contract test that invokes each registered verb with an
intentionally-wrong arg and asserts a non-zero exit code.
Why this wasn't fixed in PR #159
PR #159 surfaced the bug class but addressed it locally — by adding a
21-test signature-contract Pester suite (
verb-signature-contracts.Tests.ps1)that asserts every CLI call site in apex YAMLs matches the verb's
documented signature. That's a structural mitigation; the underlying
CLI behavior remains as-is. Fixing it properly removes a whole class
of future drift (the contract suite catches today's mistakes; the CLI
fix prevents tomorrow's).
Acceptance
failures.
docs/polyphony-cli-reference.md.Lineage
docs/polyphony-state-effects-catalog.md"Open questions" sectionreferences this.