Skip to content

[#106] Added choice field options that resolve from the collected answers. - #137

Merged
AlexSkrypnyk merged 7 commits into
mainfrom
feature/106-dynamic-options
Jul 28, 2026
Merged

[#106] Added choice field options that resolve from the collected answers.#137
AlexSkrypnyk merged 7 commits into
mainfrom
feature/106-dynamic-options

Conversation

@AlexSkrypnyk

@AlexSkrypnyk AlexSkrypnyk commented Jul 28, 2026

Copy link
Copy Markdown
Member

Closes #106

Summary

A callback handed to a choice field's ->options() now routes by its own signature: one that takes a Context parameter follows the collected answers and is re-resolved whenever they change, while one that takes no parameters keeps the existing load-once lazy behavior.
Resolution runs inside the engine's settling fixpoint (Engine::stabilize()), before conditions, derives and fix-ups, so the interactive panel, headless collection, the generated schema and the validator all see the same narrowed list.
A value the narrowed set no longer holds is reconciled away - dropped from a select or multi-select, a reorder ranking completed to a full permutation, a toggle returned to its first state - except a value supplied headlessly, which the input guard reports instead of silently discarding.
The schema now advertises an options_dynamic flag per field, and Tui::validate() takes an optional run context so membership checks resolve against the same answers being validated.

Changes

  • Engine (src/Engine/Engine.php): added resolveDynamicOptions() to resolve answer-driven options inside the settling fixpoint, suppliedInputs() to track which fields were answered headlessly, and optionsError() to wrap consumer exceptions into an EngineException; stabilize(), settle(), resolveState() and collect() now thread a Context and a per-field "supplied" map through, and resolved rows are memoised per field against the answers plus the run context so a settle that changes nothing does not re-call the resolver.
  • Field declaration (src/Builder/FieldBuilder.php, src/Model/Field.php): options() inspects the closure's arity with ReflectionFunction and routes a zero-argument callback to the existing $optionsLoader and a callback taking a parameter to the new $optionsResolver; Field gained hasSettledOptions(), hasDynamicOptions() and reconcileValue(), plus build-time guards rejecting options declared on a type with no list (FieldType::supportsOptions()) or a resolver declared alongside a fixed list, a loader or a query source.
  • Form build-time checks (src/Builder/Form.php): the toggle and reorder option checks now skip fields whose options have not yet settled (loader, resolver or query source), since those rows arrive later rather than at build time.
  • Schema surfaces (src/Schema/OptionsResolver.php, SchemaGenerator.php, SchemaValidator.php, AgentHelp.php): a new Schema\OptionsResolver resolves a field's answer-driven options against a caller-supplied context, used by SchemaGenerator (which also flags each field options_dynamic), AgentHelp and the now context-aware SchemaValidator, which checks option membership against the set the answers under validation resolve to.
  • Facade and panel (src/Tui.php, src/Render/PanelController.php): Tui::validate() takes an optional Context; PanelController accepts the facade's already-constructed Engine and the run Context so the same engine resolves both the initial state and every interactive re-settle.
  • Docs and playground: a new "Options from the answers" section in docs/content/field-behaviour.mdx, cross-links from progress.mdx and widgets/select.mdx, a runnable playground/19-dynamic-options.php, README and playground README rows, and both dataflow diagrams (.puml sources and regenerated .svg) updated to show the new resolution step.
  • Tests: tests/phpunit/Unit/DynamicOptionsTest.php covers resolution, per-type reconciliation, headless rejection, interactive narrowing, memoisation, and schema/validator/AgentHelp integration, plus build-time guard rejections; EngineConditionalTest and SchemaGeneratorTest are updated for the new settle() signature and the options_dynamic field.

Before / After

BEFORE - one loader signature, resolved once, seen only by the panel
──────────────────────────────────────────────────────────────────────

  ->options(['carrot' => 'Carrot', 'potato' => 'Potato'])     fixed list
  ->options(fn(): array => fetchCatalog())                    loader, called once when the panel opens

                                    │
                                    ▼
                          ┌─────────────────────┐
                          │   Field::$options    │   one static list, cached forever
                          └─────────────────────┘


AFTER - the callback's signature routes it; a resolver follows the answers
────────────────────────────────────────────────────────────────────────────

  ->options(fn(): array => fetchCatalog())                                loader    (0 args, unchanged: once, on panel open)
  ->options(fn(Context $c): array => $catalog[$c->answers['category']])   resolver  (>=1 arg, new)

                                    │
                                    │ re-run every time the answers change
                                    ▼
                    ┌────────────────────────────────────┐
                    │    Engine::stabilize()  (fixpoint)   │
                    │    options → derive → conditions     │
                    └───────────────────┬───────────────────┘
                                        │ one narrowed list, shared by:
              ┌────────────┬───────────┼────────────┬─────────────┐
              ▼            ▼           ▼            ▼             ▼
           panel       collect()   schema()     validate()    agentHelp()
         (editor)      (headless)  (options_    (membership   (enumerated
                                     dynamic)     check)         values)

  A value the narrowed set drops is reconciled - cleared, a ranking completed,
  a toggle returned to its first state - unless it was supplied headlessly,
  which the input guard reports instead of silently discarding it.

Summary by CodeRabbit

  • New Features

    • Added answer-driven options that dynamically narrow field choices based on previous answers.
    • Dynamic options now update interactively, validate submitted values, and reconcile selections when available choices change.
    • Schemas and agent help now expose resolved options and identify dynamic option fields.
    • Added a playground example demonstrating dependent selections.
  • Documentation

    • Added guidance and examples for configuring and using dynamic options across forms and widgets.
  • Tests

    • Added comprehensive coverage for resolution, validation, contexts, interactive updates, and field-specific behavior.

@AlexSkrypnyk AlexSkrypnyk added this to the 0.2.0 milestone Jul 28, 2026
@github-actions

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 061884de-e298-4b22-9fb8-60ca866a1f70

📥 Commits

Reviewing files that changed from the base of the PR and between 4f5f5ba and 460dc87.

⛔ Files ignored due to path filters (4)
  • docs/architecture/dataflow-collect-dark.svg is excluded by !**/*.svg
  • docs/architecture/dataflow-collect.svg is excluded by !**/*.svg
  • docs/architecture/dataflow-tui-dark.svg is excluded by !**/*.svg
  • docs/architecture/dataflow-tui.svg is excluded by !**/*.svg
📒 Files selected for processing (22)
  • README.md
  • docs/architecture/dataflow-collect.puml
  • docs/architecture/dataflow-tui.puml
  • docs/content/field-behaviour.mdx
  • docs/content/progress.mdx
  • docs/content/widgets/select.mdx
  • playground/19-dynamic-options.php
  • playground/README.md
  • src/Builder/FieldBuilder.php
  • src/Builder/Form.php
  • src/Engine/Engine.php
  • src/Model/Field.php
  • src/Model/FieldType.php
  • src/Render/PanelController.php
  • src/Schema/AgentHelp.php
  • src/Schema/OptionsResolver.php
  • src/Schema/SchemaGenerator.php
  • src/Schema/SchemaValidator.php
  • src/Tui.php
  • tests/phpunit/Unit/DynamicOptionsTest.php
  • tests/phpunit/Unit/Engine/EngineConditionalTest.php
  • tests/phpunit/Unit/Schema/SchemaGeneratorTest.php
📝 Walkthrough

Walkthrough

Adds answer-driven dynamic options for choice fields. Resolvers receive collected answers and context, run during stabilization and validation, reconcile changed values, and expose resolved options through interactive forms, schemas, agent help, documentation, and examples.

Changes

Answer-driven dynamic options

Layer / File(s) Summary
Dynamic option field contract
src/Builder/FieldBuilder.php, src/Model/Field.php, src/Model/FieldType.php, src/Builder/Form.php
Parameterized options() callbacks are stored as resolvers, validated against supported field types, and reconciled for multi-choice, reorder, and toggle fields.
Option resolution and interactive settling
src/Engine/Engine.php, src/Render/PanelController.php, src/Tui.php, docs/architecture/*.puml
Engine stabilization resolves and memoizes options from answers and context, while interactive settling passes context and reapplies derived values, conditions, and fix-ups.
Validation and schema surfaces
src/Schema/*.php, src/Tui.php
Validation, schema generation, and agent help resolve dynamic options before membership checks or option output; generated prompts include options_dynamic.
Examples, documentation, and regression coverage
tests/phpunit/Unit/DynamicOptionsTest.php, tests/phpunit/Unit/Engine/EngineConditionalTest.php, tests/phpunit/Unit/Schema/SchemaGeneratorTest.php, docs/content/*, playground/*, README.md
Tests cover resolution, reconciliation, contexts, interactive narrowing, validation, schemas, and help output; documentation and playground listings describe the feature.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PanelController
  participant Engine
  participant OptionsResolver
  participant Field
  PanelController->>Engine: settle values with context
  Engine->>OptionsResolver: resolve options from answers
  OptionsResolver->>Field: update effective option set
  Engine->>Field: reconcile changed value
  Engine-->>PanelController: settled values and active fields
Loading

Possibly related PRs

  • drevops/tui#114: Modifies the same option-loading machinery for lazy option behavior.
  • drevops/tui#80: Shares the interactive settle-after-edit engine flow.
  • drevops/tui#28: Overlaps with toggle-specific option validation and value handling.

Suggested labels: A3

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely describes the main change: choice field options resolved from collected answers.
Linked Issues check ✅ Passed The PR satisfies #106 with answer-driven option closures, validation, interactive updates, schema support, docs, and tests.
Out of Scope Changes check ✅ Passed The changes are all aligned with dynamic options support and its documentation, examples, and tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/106-dynamic-options

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.46835% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.09%. Comparing base (042ccaf) to head (460dc87).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/Builder/Form.php 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #137      +/-   ##
==========================================
- Coverage   99.12%   99.09%   -0.04%     
==========================================
  Files         118      119       +1     
  Lines        4580     4645      +65     
==========================================
+ Hits         4540     4603      +63     
- Misses         40       42       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

@github-actions

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Render/PanelController.php (1)

862-880: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

A throwing optionsResolver crashes the whole interactive session instead of degrading gracefully.

resettle() now runs resolveDynamicOptions() via Engine::settle(), which throws EngineException if a field's answer-driven optionsResolver closure throws. resettle() is called from the constructor, every accepted edit, panel entry, and modal close - none of these paths catch this exception, so it propagates out of run() (and Tui::interact()), ending the session and discarding the answers collected so far.

Query-driven options (optionsSource, used by search/suggest) already handle this case gracefully: resolveQuery() catches \Throwable and shows an inline "Could not load options." message via failQuery() instead of crashing. Answer-driven resolvers deserve the same treatment, especially since resettle() fires on every edit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Render/PanelController.php` around lines 862 - 880, Update resettle() to
catch Throwable from engine->settle(), preserving the current answers and
interactive session instead of allowing an optionsResolver failure to escape.
Handle the failure through the existing inline options-error mechanism used by
resolveQuery()/failQuery(), while retaining normal active/value updates and
cursor clamping when settling succeeds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/field-behaviour.mdx`:
- Line 105: Update the callback timing description in the field-behaviour
documentation to distinguish interactive and headless usage: zero-argument
loaders resolve once when the interactive panel opens, while headless collection
resolves the loader up front. Preserve the existing return-value and context
details.

In `@docs/content/widgets/select.mdx`:
- Around line 33-39: Update the options row in the select widget API table to
avoid marking options() itself as required, since repeated option() calls
populate the same choices set. Mark the choices requirement instead, or change
options() to optional while preserving the existing description of both
supported configuration methods.

In `@README.md`:
- Line 70: Qualify the dynamic-options documentation to state that narrowed
option sets remove stale collected values, but explicitly headlessly supplied
invalid values remain available for collection to reject with an
EngineException. Update README.md line 70, the docblock in
playground/19-dynamic-options.php lines 7-12, and the matching summary in
playground/README.md line 36 consistently.

---

Outside diff comments:
In `@src/Render/PanelController.php`:
- Around line 862-880: Update resettle() to catch Throwable from
engine->settle(), preserving the current answers and interactive session instead
of allowing an optionsResolver failure to escape. Handle the failure through the
existing inline options-error mechanism used by resolveQuery()/failQuery(),
while retaining normal active/value updates and cursor clamping when settling
succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e828416a-542b-46f7-9da3-cb84d055e2be

📥 Commits

Reviewing files that changed from the base of the PR and between 6843554 and 4f5f5ba.

⛔ Files ignored due to path filters (4)
  • docs/architecture/dataflow-collect-dark.svg is excluded by !**/*.svg
  • docs/architecture/dataflow-collect.svg is excluded by !**/*.svg
  • docs/architecture/dataflow-tui-dark.svg is excluded by !**/*.svg
  • docs/architecture/dataflow-tui.svg is excluded by !**/*.svg
📒 Files selected for processing (22)
  • README.md
  • docs/architecture/dataflow-collect.puml
  • docs/architecture/dataflow-tui.puml
  • docs/content/field-behaviour.mdx
  • docs/content/progress.mdx
  • docs/content/widgets/select.mdx
  • playground/19-dynamic-options.php
  • playground/README.md
  • src/Builder/FieldBuilder.php
  • src/Builder/Form.php
  • src/Engine/Engine.php
  • src/Model/Field.php
  • src/Model/FieldType.php
  • src/Render/PanelController.php
  • src/Schema/AgentHelp.php
  • src/Schema/OptionsResolver.php
  • src/Schema/SchemaGenerator.php
  • src/Schema/SchemaValidator.php
  • src/Tui.php
  • tests/phpunit/Unit/DynamicOptionsTest.php
  • tests/phpunit/Unit/Engine/EngineConditionalTest.php
  • tests/phpunit/Unit/Schema/SchemaGeneratorTest.php

Comment thread docs/content/field-behaviour.mdx Outdated
Comment thread docs/content/widgets/select.mdx
Comment thread README.md Outdated
Choice fields take an 'optionsFor()' resolver called with the run context and re-run whenever the answers change. The set resolves inside the engine's settling, so conditions, headless collection, the schema and the validator all see the same narrowed list, and a value the set no longer holds is reconciled away - a ranking completed, a toggle returned to its first state - unless it was supplied, in which case it is reported.
…oth lifecycles.

A callback handed to 'options()' now says by its own signature when it runs: one asking for the run context follows the collected answers, one asking for nothing loads once on panel entry as before. Options declared on a type with no list are rejected when the form is built, whichever form they take. Adds the playground demo, the documentation and the updated data-flow diagrams.
The engine memo now keys on the whole resolver input, so a second run against another directory or in update mode is not answered from the first one's cached options. 'Tui::validate()' takes the run context its sibling schema calls already take and passes it down, and a schema-side resolver that cannot answer empties the list rather than advertising the set some earlier context produced. An exception code that is not an integer coerces instead of failing the report. Reads an answer defensively in the demo and the documented example, since a supplied value is whatever arrived until it is validated.
…assertions.

The static analyser reads a generated schema as nested 'mixed', and 'assertIsArray()' does not narrow it without the PHPUnit extension, so the helpers guard with 'is_array()' and 'is_string()' and fail with a message naming the prompt.
…ames.

Renames the two test methods whose single-letter word ran into the next capital, collapses the option guard into one condition, and rewraps a comment inside the eighty-column limit.
Emphasis uses underscores and the option table columns are padded to the widest cell, as the formatter writes them.
…lied-value exception.

The loader paragraph now covers headless collection resolving it up front, and the summaries in the README, the playground index and the demo docblock say that a value supplied headlessly is reported rather than dropped.
@AlexSkrypnyk
AlexSkrypnyk force-pushed the feature/106-dynamic-options branch from 4f5f5ba to 460dc87 Compare July 28, 2026 11:02
@github-actions

Copy link
Copy Markdown
Code Coverage Report:
  2026-07-28 11:05:18

 Summary:
  Classes: 84.03% (100/119)
  Methods: 96.79% (1024/1058)
  Lines:   99.10% (4603/4645)

DrevOps\Tui\Answers\Answer
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Answers\Answers
  Methods: 100.00% (10/10)   Lines: 100.00% ( 21/ 21)
DrevOps\Tui\Answers\Provenance
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Answers\SummaryFormatter
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 22/ 22)
DrevOps\Tui\Answers\ValueFormatter
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  6/  6)
DrevOps\Tui\Builder\FieldBuilder
  Methods:  98.33% (59/60)   Lines:  98.70% (227/230)
DrevOps\Tui\Builder\Form
  Methods:  84.62% (11/13)   Lines:  95.45% ( 63/ 66)
DrevOps\Tui\Builder\LayoutGuard
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Builder\PanelBuilder
  Methods: 100.00% (25/25)   Lines: 100.00% ( 44/ 44)
DrevOps\Tui\Condition\CompositeCondition
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 19/ 19)
DrevOps\Tui\Condition\Condition
  Methods: 100.00% (10/10)   Lines: 100.00% ( 38/ 38)
DrevOps\Tui\Derive\Derive
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 10/ 10)
DrevOps\Tui\Derive\Deriver
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 13/ 13)
DrevOps\Tui\Derive\Transform
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Discovery\AbstractDiscover
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Discovery\Dotenv
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 19/ 19)
DrevOps\Tui\Discovery\JsonValue
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 16/ 16)
DrevOps\Tui\Discovery\PathExists
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  3/  3)
DrevOps\Tui\Discovery\Scan
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Engine\Engine
  Methods: 100.00% (24/24)   Lines: 100.00% (187/187)
DrevOps\Tui\Handler\HandlerRegistry
  Methods:  85.71% ( 6/ 7)   Lines:  95.45% ( 21/ 22)
DrevOps\Tui\Input\Binding
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Input\DefaultKeyMap
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 43/ 43)
DrevOps\Tui\Input\Hint
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  2/  2)
DrevOps\Tui\Input\Key
  Methods:  87.50% ( 7/ 8)   Lines:  63.64% (  7/ 11)
DrevOps\Tui\Input\KeyMap
  Methods: 100.00% (11/11)   Lines: 100.00% ( 61/ 61)
DrevOps\Tui\Input\KeyMapManager
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Input\KeyParser
  Methods:  85.71% ( 6/ 7)   Lines:  98.95% ( 94/ 95)
DrevOps\Tui\Input\Scope
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 17/ 17)
DrevOps\Tui\Input\ScopedKeyMap
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% (  4/  4)
DrevOps\Tui\Input\VimKeyMap
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Model\Buttons
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Model\DateBounds
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 28/ 28)
DrevOps\Tui\Model\Field
  Methods:  90.91% (20/22)   Lines:  98.61% (142/144)
DrevOps\Tui\Model\FieldType
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 45/ 45)
DrevOps\Tui\Model\FilePickerConstraints
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 53/ 53)
DrevOps\Tui\Model\FormDefinition
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 31/ 31)
DrevOps\Tui\Model\Modal
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  2/  2)
DrevOps\Tui\Model\NumberBounds
  Methods:  83.33% ( 5/ 6)   Lines:  95.24% ( 20/ 21)
DrevOps\Tui\Model\Option
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Model\Panel
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  3/  3)
DrevOps\Tui\Model\SelectionBounds
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 23/ 23)
DrevOps\Tui\Model\TableSpec
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  9/  9)
DrevOps\Tui\Model\Template
  Methods: 100.00% (19/19)   Lines: 100.00% ( 67/ 67)
DrevOps\Tui\Model\Weekday
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Primitive\Output
  Methods: 100.00% (16/16)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Primitive\Progress
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 28/ 28)
DrevOps\Tui\Primitive\ProgressReporter
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  2/  2)
DrevOps\Tui\Render\Ansi
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 22/ 22)
DrevOps\Tui\Render\Box
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Render\ExternalEditor
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 27/ 27)
DrevOps\Tui\Render\HelpSection
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Render\Markup
  Methods: 100.00% (10/10)   Lines: 100.00% ( 69/ 69)
DrevOps\Tui\Render\MarkupSegment
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Render\Navigator
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 16/ 16)
DrevOps\Tui\Render\Overlay
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 24/ 24)
DrevOps\Tui\Render\PanelController
  Methods:  98.11% (52/53)   Lines:  99.74% (377/378)
DrevOps\Tui\Render\Scroller
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 14/ 14)
DrevOps\Tui\Render\Table
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 64/ 64)
DrevOps\Tui\Render\Terminal
  Methods:  95.65% (22/23)   Lines:  96.97% ( 64/ 66)
DrevOps\Tui\Render\TerminalControl
  Methods: 100.00% (11/11)   Lines: 100.00% ( 11/ 11)
DrevOps\Tui\Render\Viewport
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Resolver\EnvNameResolver
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Resolver\InputResolver
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 30/ 30)
DrevOps\Tui\Schema\AgentHelp
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 67/ 67)
DrevOps\Tui\Schema\DefaultResolver
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Schema\OptionsResolver
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Schema\SchemaGenerator
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 46/ 46)
DrevOps\Tui\Schema\SchemaValidator
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 40/ 40)
DrevOps\Tui\Testing\ArrayKeyStream
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Testing\BufferedTerminal
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Testing\KeyEncoder
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 22/ 22)
DrevOps\Tui\Testing\TuiTester
  Methods: 100.00% (14/14)   Lines: 100.00% ( 36/ 36)
DrevOps\Tui\Testing\WidgetRunner
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Theme\DefaultTheme
  Methods:  96.99% (129/133)   Lines:  99.27% (676/681)
DrevOps\Tui\Theme\DosTheme
  Methods:  81.25% (13/16)   Lines:  83.33% ( 15/ 18)
DrevOps\Tui\Theme\EmberTheme
  Methods:  66.67% ( 6/ 9)   Lines:  66.67% (  6/  9)
DrevOps\Tui\Theme\FrostTheme
  Methods:  66.67% ( 6/ 9)   Lines:  66.67% (  6/  9)
DrevOps\Tui\Theme\MidnightTheme
  Methods:  66.67% ( 6/ 9)   Lines:  66.67% (  6/  9)
DrevOps\Tui\Theme\MonoTheme
  Methods:  66.67% ( 6/ 9)   Lines:  66.67% (  6/  9)
DrevOps\Tui\Theme\Sgr
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Theme\ThemeManager
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Translation\Translator
  Methods: 100.00% (18/18)   Lines: 100.00% ( 87/ 87)
DrevOps\Tui\Tui
  Methods: 100.00% (29/29)   Lines: 100.00% ( 99/ 99)
DrevOps\Tui\Utils\Strings
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 36/ 36)
DrevOps\Tui\Widget\AbstractWidget
  Methods:  95.00% (19/20)   Lines:  98.33% ( 59/ 60)
DrevOps\Tui\Widget\CalendarWidget
  Methods: 100.00% (13/13)   Lines: 100.00% ( 52/ 52)
DrevOps\Tui\Widget\Capability\CompletionCapableTrait
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 16/ 16)
DrevOps\Tui\Widget\Capability\FilterCapableTrait
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Widget\Capability\OptionsCapableTrait
  Methods: 100.00% (10/10)   Lines: 100.00% ( 36/ 36)
DrevOps\Tui\Widget\Capability\PagingCapableTrait
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 16/ 16)
DrevOps\Tui\Widget\Capability\PlaceholderCapableTrait
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Widget\Capability\QueryOptionsCapableTrait
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 35/ 35)
DrevOps\Tui\Widget\Capability\SearchCapableTrait
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  3/  3)
DrevOps\Tui\Widget\Capability\SelectionBoundedTrait
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Widget\Capability\SelectionCapableTrait
  Methods: 100.00% (13/13)   Lines: 100.00% ( 88/ 88)
DrevOps\Tui\Widget\Capability\TextEditCapableTrait
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 34/ 34)
DrevOps\Tui\Widget\ConfirmWidget
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 22/ 22)
DrevOps\Tui\Widget\FilePickerWidget
  Methods: 100.00% (33/33)   Lines: 100.00% (187/187)
DrevOps\Tui\Widget\MatchResult
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Widget\MatchTier
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  6/  6)
DrevOps\Tui\Widget\Matcher
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 73/ 73)
DrevOps\Tui\Widget\NumberWidget
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 36/ 36)
DrevOps\Tui\Widget\PasswordDisplay
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Widget\PasswordWidget
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 46/ 46)
DrevOps\Tui\Widget\PauseWidget
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 11/ 11)
DrevOps\Tui\Widget\RatingWidget
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 25/ 25)
DrevOps\Tui\Widget\ReorderWidget
  Methods: 100.00% (11/11)   Lines: 100.00% ( 58/ 58)
DrevOps\Tui\Widget\SearchWidget
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 16/ 16)
DrevOps\Tui\Widget\SelectWidget
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Widget\SuggestWidget
  Methods: 100.00% (19/19)   Lines: 100.00% ( 73/ 73)
DrevOps\Tui\Widget\TemplateWidget
  Methods: 100.00% (14/14)   Lines: 100.00% ( 63/ 63)
DrevOps\Tui\Widget\TextWidget
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 18/ 18)
DrevOps\Tui\Widget\TextareaWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 49/ 49)
DrevOps\Tui\Widget\ToggleWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 30/ 30)
DrevOps\Tui\Widget\WidgetFactory
  Methods: 100.00% (13/13)   Lines: 100.00% ( 59/ 59)

@AlexSkrypnyk AlexSkrypnyk added the Needs review Pull request needs a review from assigned developers label Jul 28, 2026
@AlexSkrypnyk
AlexSkrypnyk merged commit 372ea7e into main Jul 28, 2026
12 checks passed
@AlexSkrypnyk
AlexSkrypnyk deleted the feature/106-dynamic-options branch July 28, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs review Pull request needs a review from assigned developers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support dynamic options resolved from collected answers in choice fields

1 participant