Skip to content

add: prompt-flag compatibility validation#12

Merged
kjanat merged 12 commits intomasterfrom
claude/fix-issue-11-sLddT
Apr 11, 2026
Merged

add: prompt-flag compatibility validation#12
kjanat merged 12 commits intomasterfrom
claude/fix-issue-11-sLddT

Conversation

@kjanat
Copy link
Copy Markdown
Owner

@kjanat kjanat commented Apr 8, 2026

Summary

Add compile-time and runtime validation to ensure prompt kinds are compatible with their corresponding flag types. This prevents invalid configurations like using multiselect prompts with enum flags or confirm prompts with string flags.

Key Changes

  • Type-level validation: Introduced AllowedPromptConfig<C> type that constrains which prompt kinds are allowed for each flag type:

    • Boolean flags → confirm only
    • Number flags → input only
    • String/enum flags → input or select
    • Array flags → multiselect only
    • Custom flags → all prompt kinds allowed
  • Runtime validation: Added validatePromptFlagCompatibility() function that checks prompt-flag compatibility during resolution and returns a ValidationError with helpful suggestions if there's a mismatch

  • Updated FlagBuilder.prompt() signature: Changed parameter type from PromptConfig to AllowedPromptConfig<C> to enforce type safety at the API level

  • Comprehensive test coverage: Added 9 test cases covering:

    • Invalid combinations (multiselect on enum, input on boolean, etc.)
    • Error message validation (includes flag name and suggested kind)
    • Early rejection before prompting the user
    • Type-level constraint verification

Implementation Details

  • Validation happens before prompting to fail fast without user interaction
  • Error messages include the flag name, incompatible prompt kind, and suggested alternative
  • Uses a COMPATIBLE_PROMPT_KINDS lookup table to define valid combinations
  • Maintains backward compatibility for custom flag types (allows all prompt kinds)

https://claude.ai/code/session_01Xsmxjjw5T6t2jPLUdNMgDM

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 8, 2026

Open in StackBlitz

bun add https://pkg.pr.new/@kjanat/dreamcli@12
npm i https://pkg.pr.new/@kjanat/dreamcli@12

commit: 4f629e1

@kjanat kjanat self-assigned this Apr 8, 2026
@coderabbitai coderabbitai bot added kind: feature New capability or product enhancement kind: breaking Breaking API, runtime, or packaging change area: schema Schema builders, schema DSL, and JSON Schema export area: resolve Argv, env, config, prompt, and default resolution labels Apr 8, 2026
@kjanat kjanat force-pushed the claude/fix-issue-11-sLddT branch from 1693b7e to c31b5d7 Compare April 8, 2026 21:50
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 269e8c55-b252-4e26-be2c-31edf7c68380

📥 Commits

Reviewing files that changed from the base of the PR and between 28705fc and 4f629e1.

📒 Files selected for processing (1)
  • src/core/resolve/resolve-prompt.test.ts
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx,json,md}

📄 CodeRabbit inference engine (AGENTS.md)

Use tabs for indentation

Files:

  • src/core/resolve/resolve-prompt.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use single quotes for string literals in TypeScript and JavaScript
Use semicolons at the end of statements

Files:

  • src/core/resolve/resolve-prompt.test.ts
**/*

📄 CodeRabbit inference engine (AGENTS.md)

Use LF (Unix) line endings

Files:

  • src/core/resolve/resolve-prompt.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use import type for type-only imports in TypeScript
Include .ts extensions in all relative imports
Enforce strict TypeScript everywhere; use exactOptionalPropertyTypes with conditional spreads
Use explicit named re-exports only; do not use export *

Files:

  • src/core/resolve/resolve-prompt.test.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Add @module JSDoc at top of source files; mark non-public API with @internal

Files:

  • src/core/resolve/resolve-prompt.test.ts
src/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/core/**/*.{ts,tsx}: Keep public API factory-first: cli(), command(), flag.*(), createOutput(), createAdapter()
Do not use process.* or runtime-specific APIs in src/core/

Files:

  • src/core/resolve/resolve-prompt.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.ts: Co-locate tests as *.test.ts files; use describe() + it() with em dashes in suite titles and // === and // --- section markers
Do not use lifecycle hooks, snapshots, or module mocks in tests
Include trailing newline in output assertions

Files:

  • src/core/resolve/resolve-prompt.test.ts
src/core/{cli,completion,output,prompt,resolve}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use direct-file imports instead of barrel imports in cli/, completion/, output/, prompt/, resolve/, and runtime/ to avoid circular dependencies

Files:

  • src/core/resolve/resolve-prompt.test.ts
src/core/resolve/**/*{flags,args,resolve}*.ts

📄 CodeRabbit inference engine (src/core/resolve/AGENTS.md)

Collect DeprecationWarning structs during resolution when deprecated flag or arg usage is detected, and surface these alongside normal validation results.

Files:

  • src/core/resolve/resolve-prompt.test.ts
src/core/resolve/**/*.test.ts

📄 CodeRabbit inference engine (src/core/resolve/AGENTS.md)

Organize resolve test files by aspect (core logic, errors, env, config, prompt, interactive, integration, aggregation, arg-env, stdin, contracts, property) with naming convention resolve-*.test.ts or *-test.ts.

Files:

  • src/core/resolve/resolve-prompt.test.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
URL: 
File: src/core/resolve/AGENTS.md:undefined-undefined
Timestamp: 2026-04-09T16:38:23.343Z
Learning: Maintain the resolution order chain: CLI argv -> environment variable -> config file -> interactive prompt -> default value. Each source is tried in order, with the first non-undefined value winning.
Learnt from: CR
URL: 
File: src/core/resolve/AGENTS.md:undefined-undefined
Timestamp: 2026-04-09T16:38:23.343Z
Learning: Avoid circular dependencies by importing `schema/prompt.ts` directly in resolve module files rather than through a barrel export.
Learnt from: CR
URL: 
File: src/core/schema/AGENTS.md:undefined-undefined
Timestamp: 2026-04-09T16:38:35.628Z
Learning: When adding a new flag type: add variant to FlagKind union, add factory method, update InferFlag, wire through resolve/coerce.ts, and add tests
Learnt from: CR
URL: 
File: src/core/schema/AGENTS.md:undefined-undefined
Timestamp: 2026-04-09T16:38:35.628Z
Learning: Implement CommandBuilder as multi-file module in core/ with single index.ts barrel export; other modules (except resolve, output, completion) use single index.ts pattern
📚 Learning: 2026-02-11T11:57:27.623Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 3
File: src/core/cli/cli-completion-e2e.test.ts:9-16
Timestamp: 2026-02-11T11:57:27.623Z
Learning: Enforce explicit .ts extensions in all relative imports for TypeScript source files (enabled by allowImportingTsExtensions and noEmit in tsconfig.json). This applies to both static and dynamic imports. For each TypeScript file, update relative import paths to end with .ts (e.g., './utils/helper' -> './utils/helper.ts'). Ensure tooling (linting/eslint rules or a codemod) flags missing extensions and provides a quick fix. This guideline should cover all TypeScript files in the repo, not just a single test or module.

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-02-12T15:52:24.166Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 4
File: src/core/resolve/resolve-arg-env.test.ts:1-7
Timestamp: 2026-02-12T15:52:24.166Z
Learning: In test files, do not require the module JSDoc tag at the top. This rule applies to all test files, while non-test source files should still require the module tag. Use the pattern **/*.test.{ts,tsx} to cover all test TypeScript test files (adjust if your project uses only .ts tests).

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-04-02T00:14:17.279Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 7
File: src/core/cli/cli.test.ts:14-14
Timestamp: 2026-04-02T00:14:17.279Z
Learning: In kjanat/dreamcli test files (src/**/*.test.ts), do not flag differences in section separator style between `// ---` and `// ===`. This style is intentionally used by the author for all section levels (including top-level sections) in tests, so separator casing/choice should not be treated as a guideline violation in code review.

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-04-06T11:25:57.444Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 9
File: examples/spinner-progress.ts:2-12
Timestamp: 2026-04-06T11:25:57.444Z
Learning: In this repo, enforce that TypeScript source files under `src/` include the required `module` JSDoc tag (where the project’s lint/review rules expect it). Do not flag missing `module` tags in files under `examples/`, since those are documentation/example scripts (e.g., `examples/*.ts`).

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-04-06T11:26:11.173Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 9
File: examples/json-mode.ts:2-15
Timestamp: 2026-04-06T11:26:11.173Z
Learning: In this repository, `module` JSDoc tags are required for TypeScript source files under `src/` (e.g., `src/**/*.ts`). Runnable demo/example scripts under `examples/` are not part of this requirement and should not be flagged for missing `module`.

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-04-06T11:28:55.224Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 9
File: docs/.vitepress/theme/index.ts:1-16
Timestamp: 2026-04-06T11:28:55.224Z
Learning: In the kjanat/dreamcli repo, enforce the `module` JSDoc tag requirement only for TypeScript source files under `src/` (i.e., `src/**/*.ts`). Do not require `module` for files under `docs/`, including anything in `docs/.vitepress/**`, since those are part of the VitePress docs pipeline.

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-04-06T11:26:04.030Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 9
File: examples/middleware.ts:2-11
Timestamp: 2026-04-06T11:26:04.030Z
Learning: In the kjanat/dreamcli repository, require the `module` JSDoc tag only for library source files under `src/` (e.g., `src/**/*.ts`). Files outside `src/` such as runnable demo/example scripts under `examples/` are not considered source code for this rule and should not be required to include `module`.

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-04-06T18:24:44.371Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 9
File: docs/reference/api.md:8-8
Timestamp: 2026-04-06T18:24:44.371Z
Learning: In the kjanat/dreamcli repo, the explicit type annotation `Readonly<Record<string, string>>` applied to `const` object literals is an intentional documentation/intent pattern. Do not flag it as redundant or unnecessary (e.g., for being inferable) when reviewing TypeScript/TSX files; treat this explicitness as acceptable by design.

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
📚 Learning: 2026-04-06T18:40:01.263Z
Learnt from: kjanat
Repo: kjanat/dreamcli PR: 9
File: docs/.vitepress/vite-plugins/source-artifacts.ts:62-83
Timestamp: 2026-04-06T18:40:01.263Z
Learning: For this repository, Windows is explicitly not a supported/targeted platform. During code review, do not raise issues about Windows-specific path separators or normalization differences (e.g., use of `path.sep`, handling forward-slash vs backslash, or `normalize()`-related concerns). These checks should be treated as out-of-scope because the project is intended for Linux/macOS only.

Applied to files:

  • src/core/resolve/resolve-prompt.test.ts
🔇 Additional comments (1)
src/core/resolve/resolve-prompt.test.ts (1)

17-17: No issue here, matey—the export be there and proper.

Line 17 imports COMPATIBLE_PROMPT_KINDS from ./flags.ts, and that symbol is explicitly exported on line 255 of flags.ts via export { COMPATIBLE_PROMPT_KINDS, resolveFlags };. The import will type-check and run just fine. The drift-guard tests at lines 714–722 can sail on without a care in the world.

			> Likely an incorrect or invalid review comment.

📝 Walkthrough

Introduce prompt–flag kind compatibility checks (compile-time + runtime) to prevent invalid prompt configurations and fail fast (fixes #11).

Compile-time

  • Add a phantom discriminator flagKind to FlagConfig and propagate it through WithPresence so every FlagBuilder carries a literal flag kind.
  • Add and export AllowedPromptConfig<C extends FlagConfig> (via PromptConfigByFlagKind) mapping flag kinds to permitted prompt configs:
    • boolean → confirm
    • number → input
    • string / enum → input | select
    • array → multiselect
    • custom → any prompt kind
  • Tighten public API: FlagBuilder.prompt(config: PromptConfig)FlagBuilder.prompt(config: AllowedPromptConfig<C>). Update FlagFactory return types to include literal flagKind so incompatible prompt configs are rejected by TypeScript.

Runtime

  • Introduce COMPATIBLE_PROMPT_KINDS: Record<FlagKind, readonly PromptKind[]> and validatePromptFlagCompatibility(flagName, flagKind, promptKind).
  • Run validation in resolvePromptValueWithConfig(...) before resolving or invoking the prompter; on mismatch return a ValidationError (code CONSTRAINT_VIOLATED) with details (flag, flagKind, promptKind, allowed) and an actionable suggest. Prompting is skipped to fail fast.

Tests & parity guard

  • Add runtime tests asserting incompatible prompt/flag combinations are rejected, diagnostics include the flag name and suggested prompt kind, and prompting is not invoked on mismatch.
  • Add compile-time type tests for AllowedPromptConfig (expectTypeOf assertions and // @ts-expect-error`` checks) to ensure the type-level mapping enforces constraints.
  • Add a drift-guard test ensuring COMPATIBLE_PROMPT_KINDS covers the keys in FLAG_KINDS to prevent divergence between the type-level mapping and the runtime table.

Docs & tooling

  • Document the phantom flagKind, AllowedPromptConfig, and runtime validation in AGENTS.md.
  • Minor tooling tweak: meta-descriptions generator now pipes generated source to dprint via stdin (no temp file).
  • Exports changed: AllowedPromptConfig and COMPATIBLE_PROMPT_KINDS are exported; FlagConfig now includes flagKind.

Files of note

  • src/core/schema/flag.ts — phantom flagKind, AllowedPromptConfig, tightened FlagBuilder.prompt() API, updated factory return types.
  • src/core/resolve/flags.ts — COMPATIBLE_PROMPT_KINDS, validatePromptFlagCompatibility, pre-prompt validation, exported constant.
  • src/core/resolve/resolve-prompt.test.ts and src/core/schema/prompt.test.ts — runtime and type-level tests (including drift-guard).
  • scripts/build-meta-descriptions.ts, src/core/json-schema/meta-descriptions.generated.ts, CHANGELOG.md — tooling/formatting and release notes.

Impact

  • Surfaces incompatible prompt+flag configurations earlier (compile-time where possible; always before prompting), preventing delayed runtime failures and providing actionable suggestions. Public typings changed: previously-allowed invalid prompt kinds will now be rejected by TypeScript or by immediate validation errors with suggested fixes.

Walkthrough

Added compile-time and runtime enforcement tying prompt kinds to flag kinds. A compatibility map and validator (COMPATIBLE_PROMPT_KINDS, validatePromptFlagCompatibility) short-circuit prompting and return a ValidationError (code: 'CONSTRAINT_VIOLATED', details, suggest) when a prompt kind is incompatible with a flag kind.

Changes

Cohort / File(s) Summary
Schema type constraints
src/core/schema/flag.ts, src/core/schema/prompt.test.ts
Added flagKind phantom to FlagConfig, exported AllowedPromptConfig<C>, and tightened FlagBuilder.prompt(...) to accept only compatible prompt configs. Added compile-time tests for allowed/rejected prompt configs.
Runtime validation & resolution
src/core/resolve/flags.ts, src/core/resolve/resolve-prompt.test.ts, src/core/resolve/AGENTS.md
Introduced COMPATIBLE_PROMPT_KINDS and validatePromptFlagCompatibility(...); resolvePromptValueWithConfig(...) validates before prompting and returns { ok: false, error } with CONSTRAINT_VIOLATED on mismatch. Tests assert diagnostics and that prompter is not invoked; docs updated.
Tests: schema typing
src/core/schema/prompt.test.ts
Added compile-time assertions and @ts-expect-error examples verifying AllowedPromptConfig mappings and FlagBuilder.prompt constraints.
Build script tweak
scripts/build-meta-descriptions.ts
Switched dprint formatting to stdin-based invocation; removed temp-file write/read/cleanup.
Generated JSON schema formatting
src/core/json-schema/meta-descriptions.generated.ts
Formatting-only changes: normalized string quoting to double quotes in generated meta-descriptions.
Changelog & docs
CHANGELOG.md, src/core/schema/AGENTS.md
Documented compile-time/runtime compatibility enforcement and the dprint change; docs updated to describe flagKind and runtime validator.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as "CLI Resolver"
  participant Validator as "validatePromptFlagCompatibility"
  participant Resolver as "resolvePromptValueWithConfig"
  participant Prompter as "prompter.promptOne"

  CLI->>Resolver: request prompt resolution (flag, promptConfig)
  Resolver->>Validator: validate(flag.flagKind, promptConfig.kind)
  alt compatible
    Validator-->>Resolver: ok
    Resolver->>Prompter: promptOne(...)
    Prompter-->>Resolver: user value
    Resolver-->>CLI: { ok: true, value }
  else incompatible
    Validator-->>Resolver: ValidationError(CONSTRAINT_VIOLATED, details, suggest)
    Resolver-->>CLI: { ok: false, error: ValidationError }
  end
Loading

Suggested labels

kind: breaking, area: testkit

Poem

Arrr, the flags and prompts now dance by chart,
No more surprises that tear systems apart.
Types tightened, checks run fore and aft,
Prompter stays silent when configs be daft.
Shipshape, tested — now hoist the craft.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 inconclusive)

Check name Status Explanation Resolution
Goals.Md Completion Sync ❌ Error PR implements prompt-flag compatibility validation enhancing FR3 and FR4 from GOALS.md but fails to update GOALS.md to reflect these improvements. Update GOALS.md to document the new validation layer and error handling improvements in FR3/FR4 or release plan sections.
Semver Version Bump Validation ❓ Inconclusive Unable to examine PR changes or access repository files to verify version bump against code modifications. Provide access to repository structure, version files, and PR diff to assess whether version changes match the scope of code modifications.
✅ Passed checks (8 passed)
Check name Status Explanation
Title check ✅ Passed Title 'add: prompt-flag compatibility validation' follows conventional commit guidelines with lowercase 'add' prefix, is concise (41 chars), and clearly describes the feature being introduced.
Description check ✅ Passed Description is well-structured, thoroughly explains the changes, lists key modifications with concrete examples, and directly references the linked issue #11.
Linked Issues check ✅ Passed PR fully addresses linked issue #11 objectives: type-level validation via AllowedPromptConfig, runtime validation via validatePromptFlagCompatibility, error messages with suggestions, and comprehensive test coverage preventing incompatible prompt-flag combinations.
Out of Scope Changes check ✅ Passed All changes directly support prompt-flag compatibility validation: schema updates add flagKind discrimination, resolve logic adds validation gates, tests cover compatibility, documentation updates explain the feature, and build script refactoring is incidental infrastructure improvement.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
No Stale Completed Goals ✅ Passed GOALS.md exists with 478 lines and marks milestones v0.1 through v0.7 as complete. This PR does not remove or break any functionality tied to those completed goals. The interactive prompting system (v0.3) and structured error handling (v0.4) remain fully functional; the PR simply adds validation constraints to prevent invalid prompt-flag combinations before prompting occurs. All core prompt and resolve modules are intact, existing .prompt() usage throughout the codebase continues to work for valid combinations, and GOALS.md accurately reflects the current repository state.
Changelog Update ✅ Passed CHANGELOG.md properly updated under [Unreleased] with entries for prompt-flag validation, flagKind discriminator, and meta-descriptions refactoring; package.json version correctly remains 2.0.1.
Agents.Md Documentation Updated ✅ Passed Both AGENTS.md files meticulously document architectural changes: src/core/resolve/AGENTS.md covers COMPATIBLE_PROMPT_KINDS and validatePromptFlagCompatibility(), src/core/schema/AGENTS.md documents flagKind phantom discriminator and AllowedPromptConfig constraint.
📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #11

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

coderabbitai[bot]

This comment was marked as resolved.

@coderabbitai coderabbitai bot added area: build Build, CI, release automation, scripts, and packaging area: testkit In-process test harness and test utilities and removed kind: feature New capability or product enhancement kind: breaking Breaking API, runtime, or packaging change area: schema Schema builders, schema DSL, and JSON Schema export area: resolve Argv, env, config, prompt, and default resolution labels Apr 8, 2026
coderabbitai[bot]

This comment was marked as resolved.

@kjanat kjanat force-pushed the claude/fix-issue-11-sLddT branch from c31b5d7 to f86a2ae Compare April 8, 2026 22:49
@coderabbitai coderabbitai bot added kind: feature New capability or product enhancement kind: breaking Breaking API, runtime, or packaging change area: schema Schema builders, schema DSL, and JSON Schema export area: resolve Argv, env, config, prompt, and default resolution and removed area: build Build, CI, release automation, scripts, and packaging area: testkit In-process test harness and test utilities labels Apr 8, 2026
@kjanat kjanat force-pushed the claude/fix-issue-11-sLddT branch from f86a2ae to 77d991a Compare April 8, 2026 22:53
@coderabbitai coderabbitai bot added area: build Build, CI, release automation, scripts, and packaging and removed kind: feature New capability or product enhancement kind: breaking Breaking API, runtime, or packaging change area: schema Schema builders, schema DSL, and JSON Schema export area: resolve Argv, env, config, prompt, and default resolution labels Apr 8, 2026
@kjanat kjanat changed the title Validate prompt kind compatibility with flag types (issue #11) add: prompt-flag compatibility validation Apr 9, 2026
@coderabbitai coderabbitai bot added kind: breaking Breaking API, runtime, or packaging change area: build Build, CI, release automation, scripts, and packaging and removed kind: feature New capability or product enhancement area: schema Schema builders, schema DSL, and JSON Schema export area: resolve Argv, env, config, prompt, and default resolution labels Apr 9, 2026
- Add JSDoc to validatePromptFlagCompatibility(),
  resolvePromptValueWithConfig(), buildRequiredFlagSuggest(),
  and formatGeneratedSource()
- Add Unreleased changelog entries for prompt compatibility
  validation, flagKind discriminator, and stdin-based meta build
- Update resolve/AGENTS.md with compatibility map and new function
- Update schema/AGENTS.md with flagKind phantom type docs

https://claude.ai/code/session_01Xsmxjjw5T6t2jPLUdNMgDM
@coderabbitai coderabbitai bot added kind: feature New capability or product enhancement area: docs VitePress app, guides, reference pages, and docs UX area: schema Schema builders, schema DSL, and JSON Schema export area: resolve Argv, env, config, prompt, and default resolution and removed kind: breaking Breaking API, runtime, or packaging change area: build Build, CI, release automation, scripts, and packaging labels Apr 9, 2026
@kjanat kjanat marked this pull request as ready for review April 9, 2026 10:34
coderabbitai[bot]

This comment was marked as resolved.

Replace barrel import from schema/index.ts with direct imports from
command.ts and flag.ts. Fix resolve() mapping from flags.ts to index.ts.

https://claude.ai/code/session_01Xsmxjjw5T6t2jPLUdNMgDM
@coderabbitai coderabbitai bot removed kind: feature New capability or product enhancement area: docs VitePress app, guides, reference pages, and docs UX area: schema Schema builders, schema DSL, and JSON Schema export labels Apr 9, 2026
@kjanat
Copy link
Copy Markdown
Owner Author

kjanat commented Apr 9, 2026

@coderabbitai Is this a good architectural design for the repo overall, or is this a patch-job?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me dig into the actual source files before I render me verdict, aye!


🏁 Script executed:

#!/bin/bash
echo "=== src/core/schema/flag.ts ==="
cat src/core/schema/flag.ts

echo ""
echo "=== src/core/resolve/flags.ts ==="
cat src/core/resolve/flags.ts

echo ""
echo "=== src/core/schema/prompt.ts (if exists) ==="
fd -e ts prompt.ts src/core/schema/ | xargs cat 2>/dev/null || echo "not found"

echo ""
echo "=== src/core/resolve/AGENTS.md ==="
cat src/core/resolve/AGENTS.md 2>/dev/null || echo "not found"

echo ""
echo "=== src/core/schema/AGENTS.md ==="
cat src/core/schema/AGENTS.md 2>/dev/null || echo "not found"

echo ""
echo "=== Overall repo structure ==="
tree -L 4 src/ 2>/dev/null || find src -type f | head -60

Length of output: 50968


@kjanat Ahoy! After pawin' through the actual source files — this be genuine architectural work, not a patch-job. Here be me full verdict, ye scallywag:


Architectural Assessment

✅ What's done right (and done well)

1. Fits the existing phantom-type pattern perfectly

FlagConfig already carries three phantom fields — valueType, presence, optionalFallback. Adding flagKind as a fourth is idiomatic, not bolted-on. WithPresence<C, P> already propagates all three; it now propagates four. No seams show.

2. Indexed-access map is the right TypeScript pattern

type PromptConfigByFlagKind = {
  readonly string: InputPromptConfig | SelectPromptConfig;
  readonly number: InputPromptConfig;
  readonly boolean: ConfirmPromptConfig;
  readonly enum: SelectPromptConfig | InputPromptConfig;
  readonly array: MultiselectPromptConfig;
  readonly custom: PromptConfig;
};
type AllowedPromptConfig<C extends FlagConfig> = PromptConfigByFlagKind[C['flagKind']];

A conditional type chain (C['flagKind'] extends 'boolean' ? ... : C['flagKind'] extends 'number' ? ...) would've been a mess to read and extend. The indexed object + single indexed-access lookup is O(1) cognitive overhead and union-safe. Well chosen.

3. Correct three-layer enforcement model

Layer Mechanism Catches
Compile-time AllowedPromptConfig<C> on FlagBuilder.prompt() Static misconfigurations
Schema storage FlagSchema.prompt: PromptConfig (broad) Runtime-erased — correct
Runtime COMPATIBLE_PROMPT_KINDS + validatePromptFlagCompatibility() Dynamic configs, .interactive() overrides, as any bypasses

The broad PromptConfig on FlagSchema is intentional and correct — the runtime schema is type-erased. The narrow type on the builder is the authoring gate; the runtime check is the execution gate. Both are needed and both are here.

4. Fail-fast placement is correct

validatePromptFlagCompatibility() fires before prompter.promptOne() — no user ever sees a prompt they can't satisfy. And critically, it also covers interactiveConfig (the dynamic resolver path), which is the one that actually escapes the static type system.

5. Tests are aspect-split and well-rounded

Rejected combinations, exact error message content, prompter.promptOne not-called assertion, and positive allowed-combo cases. Nothing cosmetic here.


⚠️ One genuine tension to watch

PromptConfigByFlagKind (in schema/flag.ts) and COMPATIBLE_PROMPT_KINDS (in resolve/flags.ts) encode the same constraint in two places. Adding a new flag kind (e.g., a future flag.date()) requires updating both — and TypeScript won't yell if you miss the runtime table.

This is an inherent limitation of the type/runtime split in TypeScript — ye can't derive one from the other without codegen. But it's worth noting a comment pointing across files, or perhaps a unit test that asserts Object.keys(COMPATIBLE_PROMPT_KINDS) matches FLAG_KINDS, would guard against future drift. Something like:

// In a test or static assertion
import { FLAG_KINDS } from '#internals/core/schema/flag.ts';

it('COMPATIBLE_PROMPT_KINDS covers all FLAG_KINDS', () => {
  for (const kind of FLAG_KINDS) {
    expect(COMPATIBLE_PROMPT_KINDS).toHaveProperty(kind);
  }
});

Verdict

This PR extends the architecture correctly — it doesn't fight the existing patterns, it deepens them. The phantom discriminator, the indexed-access type map, the runtime mirror table, the fail-fast placement, the test coverage — all of it hangs together. Shiver me timbers, this be the right way to fix issue #11. ⚓

@kjanat

This comment was marked as low quality.

…AG_KINDS

Ensures adding a new FlagKind without updating the runtime
compatibility map is caught by tests (in addition to the
Record<FlagKind, ...> compile-time guard).

https://claude.ai/code/session_01Xsmxjjw5T6t2jPLUdNMgDM
coderabbitai[bot]

This comment was marked as resolved.

@kjanat

This comment was marked as low quality.

Assert no stale keys exist in the runtime map that aren't in FLAG_KINDS.

https://claude.ai/code/session_01Xsmxjjw5T6t2jPLUdNMgDM
coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as spam.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: testkit In-process test harness and test utilities kind: breaking Breaking API, runtime, or packaging change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prompt kind/type mismatch: multiselect accepted on enum flag, then fails at runtime

2 participants