Skip to content

feat: add intent/proposals v1 subjects and schemas#60

Open
sonra44 wants to merge 1 commit into
masterfrom
feature/orion-qiki-schemas-subjects-a
Open

feat: add intent/proposals v1 subjects and schemas#60
sonra44 wants to merge 1 commit into
masterfrom
feature/orion-qiki-schemas-subjects-a

Conversation

@sonra44
Copy link
Copy Markdown
Owner

@sonra44 sonra44 commented Jan 8, 2026

This PR introduces QIKI schemas subjects and proposals. Part of Stage 0 plan.

Summary by Sourcery

Introduce v1 QIKI intent and proposal protocol schemas and wire them into messaging subjects.

New Features:

  • Add v1 intent, proposal, proposal batch, and related enum models for the Orion QIKI protocol with strict validation rules.
  • Define dedicated NATS subjects for v1 intents and proposals while retaining a backward-compatible intent alias.

Tests:

  • Add unit tests covering round-trip serialization, required fields, strict versioning, and extra-field rejection for the new v1 protocol models.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 8, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


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

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jan 8, 2026

Reviewer's Guide

Introduces versioned QIKI intent and proposal subjects plus strict Pydantic v1 schemas for intents and proposals, along with tests covering validation, versioning, and JSON round-trips.

Class diagram for QIKI intent and proposal schemas v1

classDiagram
    class _StrictModel {
      <<pydantic.BaseModel>>
      +ConfigDict model_config
    }

    class LangHint {
      <<enum>>
      +AUTO
      +RU
      +EN
    }

    class EnvironmentMode {
      <<enum>>
      +FACTORY
      +MISSION
    }

    class SelectionV1 {
      <<model>>
      +Literal kind
      +str id
    }

    class IntentV1 {
      <<model>>
      +Literal version
      +str text
      +LangHint lang_hint
      +str screen
      +SelectionV1 selection
      +int ts
      +EnvironmentMode environment_mode
      +dict~str, Any~ snapshot_min
    }

    class ProposalV1 {
      <<model>>
      +str proposal_id
      +str title
      +str justification
      +int priority
      +float confidence
      +list~Any~ proposed_actions
      +list~Any~ _must_be_empty_in_stage_a(v)
    }

    class ProposalsBatchV1 {
      <<model>>
      +Literal version
      +int ts
      +list~ProposalV1~ proposals
      +dict~str, Any~ metadata
    }

    _StrictModel <|-- SelectionV1
    _StrictModel <|-- IntentV1
    _StrictModel <|-- ProposalV1
    _StrictModel <|-- ProposalsBatchV1

    LangHint <.. IntentV1 : uses
    EnvironmentMode <.. IntentV1 : uses

    SelectionV1 --> IntentV1 : selection
    ProposalV1 --> ProposalsBatchV1 : element of proposals
Loading

Flow diagram for QIKI intent and proposal NATS subjects v1

flowchart LR
    subgraph Versioned_subjects_v1
      QIKI_INTENT_V1[QIKI_INTENT_V1\nqiki.intent.v1]
      QIKI_PROPOSALS_V1[QIKI_PROPOSALS_V1\nqiki.proposals.v1]
    end

    QIKI_INTENTS[QIKI_INTENTS\nbackward-compat alias]

    QIKI_INTENTS --> QIKI_INTENT_V1

    IntentV1_model[IntentV1 schema]
    ProposalsBatchV1_model[ProposalsBatchV1 schema]

    IntentV1_model --> QIKI_INTENT_V1
    ProposalsBatchV1_model --> QIKI_PROPOSALS_V1
Loading

File-Level Changes

Change Details Files
Introduce versioned NATS subjects for intents and proposals while keeping a backward-compatible alias for existing intent subject.
  • Replace unversioned intent subject constant with versioned QIKI_INTENT_V1 and add QIKI_PROPOSALS_V1 for proposal messages.
  • Alias legacy QIKI_INTENTS constant to the new QIKI_INTENT_V1 value to preserve backward compatibility.
src/qiki/shared/nats_subjects.py
Add strict Pydantic v1 models for Orion QIKI protocol intents, selections, proposals, and proposal batches.
  • Define shared _StrictModel base enforcing forbidden extra fields and assignment validation.
  • Add LangHint and EnvironmentMode enums plus SelectionV1, IntentV1, ProposalV1, and ProposalsBatchV1 models with constrained types and defaults.
  • Enforce ProposalV1.proposed_actions to be empty via a field validator, as required for Stage A.
  • Include version fields fixed to Literal[1] on v1 models for strict versioning and compatibility checks.
src/qiki/shared/models/orion_qiki_protocol.py
Add unit tests validating the new Orion QIKI protocol v1 models’ behavior and JSON compatibility.
  • Test IntentV1 round-trip via model_dump/model_validate and required field enforcement.
  • Test ProposalV1 Stage A constraint that proposed_actions must be empty and that violations raise validation errors.
  • Test ProposalsBatchV1 JSON round-trip, strict version=1 enforcement, and rejection of version mismatches.
  • Test that extra fields on strict models (e.g., IntentV1) cause validation errors as expected.
tests/unit/test_orion_qiki_protocol_v1.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Consider adding a validator to SelectionV1 to enforce that id is present for all kinds except 'none' (and possibly absent for 'none'), so selection consistency is guaranteed at the schema level.
  • The ts fields in IntentV1 and ProposalsBatchV1 are plain ints; if they are always milliseconds since epoch, consider encoding that in the name (e.g., ts_ms) or via a type alias to avoid ambiguity and accidental misuse.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider adding a validator to SelectionV1 to enforce that id is present for all kinds except 'none' (and possibly absent for 'none'), so selection consistency is guaranteed at the schema level.
- The ts fields in IntentV1 and ProposalsBatchV1 are plain ints; if they are always milliseconds since epoch, consider encoding that in the name (e.g., ts_ms) or via a type alias to avoid ambiguity and accidental misuse.

## Individual Comments

### Comment 1
<location> `tests/unit/test_orion_qiki_protocol_v1.py:8-14` </location>
<code_context>
+import pytest
+from pydantic import ValidationError
+
+from qiki.shared.models.orion_qiki_protocol import (
+    EnvironmentMode,
+    IntentV1,
+    LangHint,
+    ProposalV1,
+    ProposalsBatchV1,
+    SelectionV1,
+)
+
</code_context>

<issue_to_address>
**suggestion (testing):** Add a small test to assert the QIKI_INTENTS backward‑compat alias equals QIKI_INTENT_V1

Since this PR introduces `QIKI_INTENT_V1` while keeping `QIKI_INTENTS` as a backwards-compat alias, please add a small test (here or in `test_nats_subjects.py`) asserting `QIKI_INTENTS == QIKI_INTENT_V1` to guard against them drifting apart in future changes.

Suggested implementation:

```python
from qiki.shared.models.orion_qiki_protocol import (
    EnvironmentMode,
    IntentV1,
    LangHint,
    ProposalV1,
    QIKI_INTENT_V1,
    QIKI_INTENTS,
)


def test_qiki_intents_backward_compat_alias():
    assert QIKI_INTENTS == QIKI_INTENT_V1

```

If this file already imports additional symbols (e.g. `ProposalsBatchV1`, `SelectionV1`) in the same tuple, you should merge `QIKI_INTENT_V1` and `QIKI_INTENTS` into that existing import list rather than creating duplicates. Place the `test_qiki_intents_backward_compat_alias` test alongside other simple constant/alias tests if there is an existing section for those, keeping naming consistent with the surrounding tests.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +8 to +14
from qiki.shared.models.orion_qiki_protocol import (
EnvironmentMode,
IntentV1,
LangHint,
ProposalV1,
ProposalsBatchV1,
SelectionV1,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Add a small test to assert the QIKI_INTENTS backward‑compat alias equals QIKI_INTENT_V1

Since this PR introduces QIKI_INTENT_V1 while keeping QIKI_INTENTS as a backwards-compat alias, please add a small test (here or in test_nats_subjects.py) asserting QIKI_INTENTS == QIKI_INTENT_V1 to guard against them drifting apart in future changes.

Suggested implementation:

from qiki.shared.models.orion_qiki_protocol import (
    EnvironmentMode,
    IntentV1,
    LangHint,
    ProposalV1,
    QIKI_INTENT_V1,
    QIKI_INTENTS,
)


def test_qiki_intents_backward_compat_alias():
    assert QIKI_INTENTS == QIKI_INTENT_V1

If this file already imports additional symbols (e.g. ProposalsBatchV1, SelectionV1) in the same tuple, you should merge QIKI_INTENT_V1 and QIKI_INTENTS into that existing import list rather than creating duplicates. Place the test_qiki_intents_backward_compat_alias test alongside other simple constant/alias tests if there is an existing section for those, keeping naming consistent with the surrounding tests.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55efea123e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +33 to +37
QIKI_INTENT_V1 = "qiki.intent.v1"
QIKI_PROPOSALS_V1 = "qiki.proposals.v1"

# Backward-compat alias: prefer QIKI_INTENT_V1.
QIKI_INTENTS = QIKI_INTENT_V1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep legacy subject until publisher emits IntentV1

By aliasing QIKI_INTENTS to qiki.intent.v1, existing publishers (e.g., the operator console still sends {text, source, ts_epoch}) will now emit legacy payloads on the v1 subject, which consumers validating IntentV1 will reject, and any legacy subscribers still listening to qiki.intents will stop receiving messages. Consider keeping QIKI_INTENTS as the old subject until the publisher is updated to emit the v1 schema (or keep a separate legacy constant).

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant