Repository navigation
ReqIF/Flexo requirements viewpoints (M1: vendor mbse::requirements to ufo-types) - #36
Merged
Merged
Conversation
… transparency Cherry of 249837b onto post-#34 main: - project_store.py: per-thread project (title/goal/requirements/locked, verbatim prompt log, LLM thinking log, QA history) persisted beside charts; endpoints GET /projects[/{id}], POST /projects/rename - ask_user tool: multiple-choice refinement questions as AG-UI interrupts (options ride responseSchema defaults); answers recorded as QA, run continues - system prompt workflow: THINK → ASK → 'Locked in:' → RENDER; server-side lock detection - THINKING_* events: reasoning_content as 💭 thinking blocks, captured server-side - UI: project banner (editable title, locked badge, QA, expandable prompt+thinking log) - live E2E verified: 2 refinement rounds -> locked in -> k8s panel rendered - tests: 38 python, 15 vitest
The client's state machine rejects TOOL_CALL_END/RESULT without a prior
TOOL_CALL_START ('No active tool call found'), aborting the run before the
interrupt reached the UI. ask_user now emits START → ARGS → END → RESULT;
propose_draft_change (which emitted no tool-call events at all) gets the
same lifecycle. Validated 6/6 against the @ag-ui/core event union; live
verified: question interrupt now reaches the browser and answers flow.
…e inputs
- autoResumeInterrupts: false — the client's auto-resume fired resume() the
moment an interrupt was answered, colliding with our explicit chat.send()
('A run is already in progress'); continuation is now driven solely by
submitAnswer()
- question + options render immediately (no 'Answer question' gate click);
submitAnswer targets the interrupt by id
- dark theme: composer/prompt-editor/freetext/project-title inputs got UA
white defaults on the dark app — now #091127 bg + #edf5ff text; message
bubbles, interrupt cards, error/lock/requirement chips re-themed to match
the app's palette
…o one line 'storyb00k' is an internal codename and stays out of the UI nav; the editor view's header paragraph was a wall of text — now one sentence.
…opy trimmed
- answering an ask_user interrupt now calls chat.resume(), which replays the
interrupted run with the recorded response — chat.send() started a fresh
run and the agent rejected it ('Thread has N pending interrupt(s) not
addressed by resume')
- sidebar copy: 'Example fixtures for every supported format — or clear the
source and render your own.' (internal jargon out)
The client replays identical messages on resume, so the LLM never saw the answer and re-asked. Each run now injects answered QA (and any locked-in requirements) into the system prompt as PROJECT MEMORY — 'never ask the same or an equivalent question again' — and appends unanswered QA as explicit user turns. 39/39 python (new regression test captures the second run's LLM messages and asserts the memory + answer turn are present).
Intent-first ergonomics: users don't know the diagram type they need. Gallery browse-by-use-case (filter defaults to All, distinct addressible typeIds, per-card Agent button pre-filling a typed sample prompt); agent 'discover' question series (audience -> intent shape -> fidelity -> recommend 1+1 -> confirm, with visual 'show me both') parallel to requirement refinement; type lock-in into PROJECT MEMORY; per-syntax skills loaded only when the type is locked.
Step 1 catalog: kr0ki-core/src/catalog.rs — 24 addressible diagram types with
intent tags from a controlled vocabulary, sample prompts naming the type,
discovery guide; /api/catalog route (+http test); 6 unit tests.
Step 2 gallery: intent filter bar (defaults to All, auto-resets), type cards
with Test/Edit/Agent buttons; Agent handoff prefills the Agent composer with
the type's sample prompt (never auto-sent) via forwardedProps-ready prop.
Step 3 discover mode: recommend_diagram_type tool — agent must commit to 1–2
type recommendations with rationales after ≤3 intent questions; confirm
interrupt locks lockedType into the project; REFINE vs DISCOVER mode rules in
the system prompt; project memory carries 'Type chosen: …'.
Step 4 skills: skills/types/{sequence,activity,erd,component}.md loaded ONLY
when the type is locked (replaces generic dump).
Gates: 40/40 python, 15/15 vitest, 19/19 cargo suites, just check clean.
Live acceptance: alert→resolution prompt with no syntax named → 2 discovery
questions → recommend interrupt (State diagram | Flowchart | Show me both) →
confirm locks type → next run is refine-mode with Locked in: summary.
…ads tolerated
A recommend_diagram_type interrupt (rec-*) fell into the generic
Approve-draft branch because isQuestionInterrupt only matched ask-* —
posting {approved: true} to a question endpoint 400s. rec-* now renders the
multiple-choice UI like ask-*; and the server tolerates approval-style
payloads on question interrupts by choosing the primary option. 41/41
python.
Pill-tab strip in a bordered tray: 'I want to show' label, hover/focus states, gradient active pill with glow, per-tag type counts, aria-pressed, and an empty-state when a filter matches nothing.
…headline trimmed Ten PlantUML types share one fixture, so keying Test artifacts by example id lit up every PlantUML card at once. Type cards now use their own typeStatus/typeArtifacts/typeErrors maps keyed by typeId. Hero headline cut to 'Diagram-as-code, rendered.'
- After 2 answered questions the project banner offers '⚡ Diagram now': arms fast-track server-side (POST /projects/fasttrack) and sends a best-judgement instruction. Fast-track overrides the question-mode rules entirely (FAST-TRACK MODE: asking is a contract violation); the agent states 'Best judgement:' choices and renders in the same run. - Dashboard shows a spinner + 'Generating diagram…' while a run streams, next to the panel count. - Live-verified: fast-track armed -> no questions, best-judgement lock-in, immediate render. 42/42 python, 15/15 vitest.
…s (M1) kr0ki-core no longer owns the requirements semantic model — it now re-exports ufo_types::mbse::requirements (released as ufo-types v0.15.0, PromptExecution/ufo-types#28) as kr0ki_core::requirements. Deletes the 873-line local copy; requirements_render.rs and kr0ki-server's HTTP routes are unchanged, since they resolve through the re-export. Zero duplication, zero drift: the semantic model has one home now. Verified: cargo build --workspace, clippy -D warnings, and the full kr0ki-core/kr0ki-server test suite (148 + 44 tests) all pass against the released tag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJ5M1bGQJB1VS8Epr7AGLa
Addresses PR #36 review, P1 findings 1 and 2. - crates/kr0ki-core/src/b00t_graph.rs: d2_escape_label only neutralized newlines, leaving D2's structural characters ({, }, :, ...) live in label position. A ReqIF requirement title like "normal requirement {" emits invalid D2 ("maps must be terminated with }") or, worse, a title like "{shape: circle}" gets interpreted as D2 structure instead of label text. Fixed at the shared helper (used by requirements_render, b00t_graph's own D2Emitter, and sysml_render — all three had the identical defect), so labels are now quoted/escaped the same way d2_quote already handles ids. Added adversarial-title coverage in all three call sites' test modules; updated the two existing tests that had encoded the old unquoted behavior as expected output. - .gitmodules: vendor/assistant-ui had only `branch`, no `path`/`url`, so `actions/checkout` with `submodules: recursive` fails before any test runs — blocking every CI check regardless of this PR's content. Added the missing path/url (https://github.com/elasticdotventures/ assistant-ui.git, matching the already-checked-out working tree's own remote and the pinned gitlink commit). Verified: `git ls-remote` resolves the pinned commit on that url/branch unauthenticated, and `git submodule init && git submodule sync` succeeds locally. Verified: cargo build --workspace, cargo clippy --workspace -- -D warnings, cargo test -p kr0ki-core -p kr0ki-server (150 + 44 passing). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJ5M1bGQJB1VS8Epr7AGLa
CI's cargo fmt --all -- --check caught a multi-line assert_eq! rustfmt collapses onto one line; missed locally in the prior commit since I verified build/clippy/test there but not fmt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJ5M1bGQJB1VS8Epr7AGLa
elasticdotventures
marked this pull request as ready for review
September 19, 2026 22:52
4 tasks done
elasticdotventures
added a commit
that referenced
this pull request
Sep 19, 2026
… CI (#37) * fix(docs,ci): correct stale test-count claim; wire StoryB00k/Vue into CI Addresses PR #36 review, P2 findings. - docs/HANDOFF-2026-09-19-reqif-flexo.md: "cargo test -p kr0ki-core requirements # 5 passed" went stale in the same PR that made it true — M1 moved the semantic tests to ufo-types, leaving only the D2 adapter test local. Corrected to the actual current counts for every command listed, each re-run and verified before writing it down: 4 (ufo-types' own mbse::requirements tests), 5 (new consumer-level contract tests below), 2 (requirements_render's D2-adapter tests), 1 (the server HTTP test). - crates/kr0ki-core/tests/requirements_contract.rs (new): the "consumer-level contract tests for the re-exported model/view behavior" the review asked for — an integration test exercising kr0ki_core::requirements and requirements_render::to_d2 through kr0ki-core's public API only, proving the ufo-types re-export and the one D2 adapter kr0ki-core owns actually compose the way kr0ki-server's HTTP routes depend on. 5 tests: promotion isolation, bounded impact/cycles, verification coverage, validation errors, and view-to-D2 lowering. - .github/workflows/ci.yml, justfile: wire the StoryB00k Python suite (7 files, 43 tests) and the playbook Vue/Vite suite (test + build) into `check`/`just test`, alongside the existing Rust and kr0ki-mcp-Python coverage. Both needed real fixes to actually pass, not just a wiring change: - playbook: `pnpm --dir playbook test` failed on a clean install — @assistant-ui/tap's core module graph statically imports `react` even on the Vue-only path, though tap declares react as an *optional* peer dependency. Added `react` as a playbook devDependency (upstream packaging quirk in a transitive dependency, not something fixable from this repo or the vendored assistant-ui fork). Committed playbook/pnpm-lock.yaml, which had never been committed despite pnpm being the tool actually in use (only a stale npm package-lock.json existed) — CI needs a lockfile to install deterministically with --frozen-lockfile. - StoryB00k agent: a bare `pip install` hits PEP 668 "externally-managed-environment" on Debian/Ubuntu (including GitHub's ubuntu-latest runners) unless run inside a venv. Both the CI step and `just test` now create one. Verified end-to-end locally with a clean `pnpm install --frozen-lockfile` and a fresh venv: 43 Python tests, 15 Vitest tests, and the Vite production build all pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJ5M1bGQJB1VS8Epr7AGLa * fix(storyb00k-agent): isolate test_planning_flow.py from a live kr0ki-server Wiring this suite into CI (previous commit) surfaced a real, pre-existing test-isolation gap: PlanningFlowTest mocked the LLM client but not fetch_manifest(), the one real network call server._run() makes (to KR0KI_URL, default http://127.0.0.1:8787) before ever reaching the mocked LLM. It passed locally only because a real kr0ki-server happened to be listening on that port in dev — on a clean CI runner it hit "Connection refused" and failed 4 tests + 1 assertion mismatch. test_server.py already has this exact mock (`@patch("server. fetch_manifest", return_value=[])`) on its two tests that call _run(); this applies the same pattern in setUp so it covers every test in the class. Verified with KR0KI_URL pointed at a guaranteed-unreachable port (http://127.0.0.1:1), not just by having a server happen to be up: 7/7 in this file, 43/43 in the full containers/kr0ki-storyb00k-agent suite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJ5M1bGQJB1VS8Epr7AGLa * fix(ci): drop the playbook Vite build from CI; exclude its one vendor-gated test Per review discussion on PR #37: making `pnpm --dir playbook run build` pass in CI would require building the vendored @assistant-ui/vue package's dist/, which requires installing the ENTIRE elasticdotventures/assistant-ui monorepo -- 2,347 packages, ~2GB, 2m45s (Next.js, Nuxt, SvelteKit, docs tooling, none of which kr0ki needs) -- just to produce one small package's output. Confirmed by actually running the install. Decided against taking that on right now; dropped the build step from CI and `just test`. That also means `pnpm --dir playbook test` itself fails on a clean CI checkout: assistant-ui-vendor.test.js statically imports "@assistant-ui/vue", and Vite's import-analysis can't resolve it without dist/ present -- confirmed against the actual CI failure log, not assumed. describe.skipIf can't help here since the import fails at module-load time, before any test-level skip logic runs. Fixed via a CI-conditional `exclude` in playbook/vite.config.js instead (using vitest's own configDefaults.exclude as the base, not a hand-rolled duplicate of its defaults), so the file is never even collected when CI=true. It still runs for any developer who has built the vendored package locally. Verified all three states directly, including with vendor/assistant-ui/ packages/vue/dist/ actually absent (not just unset env), matching a real clean checkout: - dist/ present, no CI env: 3 files / 15 tests pass - dist/ absent, CI=true: 2 files / 13 tests pass (vendor file skipped) - dist/ absent, no CI env: correctly fails (accurate signal for a local dev who hasn't built the vendored package -- pre-existing behavior, now just correctly scoped to not affect CI) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJ5M1bGQJB1VS8Epr7AGLa --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
docs/HANDOFF-2026-09-19-reqif-flexo.mdfor full context and boundaries.ufo-types::mbse::requirements(ufo-typesv0.15.0, PromptExecution/ufo-types#28).kr0ki-corere-exports it rather than owning a copy — zero drift, zero duplication.docs/TODO.mdupdated.reqif-opa-mcprefactor, Flexo baseline adapter, Playb00k workspace, MCP tools) is still open — seedocs/TODO.md.Test plan
cargo build --workspacecargo clippy -p kr0ki-core -p kr0ki-server -- -D warningscargo test -p kr0ki-core -p kr0ki-server(148 + 44 tests)