Skip to content

feat: spike reqif-opa-mcp over MCP + Requirement converter - #186

Open
elasticdotventures wants to merge 8 commits into
feat/zlayer-systems-model-variantfrom
feat/reqif-opa-mcp-client-spike
Open

feat: spike reqif-opa-mcp over MCP + Requirement converter#186
elasticdotventures wants to merge 8 commits into
feat/zlayer-systems-model-variantfrom
feat/reqif-opa-mcp-client-spike

Conversation

@elasticdotventures

Copy link
Copy Markdown
Member

Summary

  • Task 5 of the systems-modeling epic (ledgrrr#181 §6): spikes calling PromptExecution/reqif-opa-mcp over MCP (decision 6 — wrap over MCP, don't port to Rust) and converting its output into the Requirement node type from ledgrrr#184
  • New crate reqif-mcp-spike: McpHttpClient (minimal blocking client for reqif-opa-mcp's Streamable-HTTP MCP server — handshake, session header, the mandatory notifications/initialized, tools/call + SSE-frame result extraction), RequirementRecord (mirrors its requirement-record.schema.json exactly), and requirement_record_to_node() (the second-stage converter into arc_kit_au::node::Requirement)
  • Verified live, end-to-end, against a real checkout of reqif-opa-mcp (uv sync --extra ingest-lite, uv run python -m reqif_mcp --http --port 8123), parsing both of its own sample derived baselines — samples/standards/derived/nist_ssdf_dogfood.reqif (4 requirements) and owasp_asvs_cwe.reqif (7 requirements) — through reqif_parsereqif_queryrequirement_record_to_nodeRequirement::node_id(), producing correct, deterministic req: NodeIds for all 11
  • That live run is captured as tests/live_server.rs, #[ignore]'d by default (needs an external repo checkout + Python/uv that CI/a fresh clone won't have) — re-run manually with REQIF_MCP_URL set to a running server

Field mapping (documented in the module doc comment)

uidrequirement_id, keytitle, textrationale, attrs.source_standard(+source_url) falling back to policy_baseline.idsource, status passed through, related_decisions always empty (reqif-opa-mcp carries no decision links — those get created later in arc-kit-au itself).

Stacked on #185#184#183 (same epic branch chain).

Test plan

  • cargo test -p reqif-mcp-spike — 5 unit tests pass, 1 ignored (live-server test)
  • Live run against both real sample baselines (see commit message) — all 11 requirements converted correctly
  • cargo check --workspace --all-features — clean (1 pre-existing unrelated warning in ledgerr-mcp/src/fbar.rs)
  • cargo clippy -p reqif-mcp-spike --all-targets -- -D warnings — clean

https://claude.ai/code/session_01XUAuQ6291UNn9sWcaviosB

…t converter

Spike per decision 6 (docs/systems-modeling-registry-rescope.md §5/§6
task 5): reqif-opa-mcp is wrapped over MCP, not ported to Rust;
arc-kit-au stays the canonical decision+cost ledger.

New crate reqif-mcp-spike:
- McpHttpClient: minimal blocking client for reqif-opa-mcp's Streamable-
  HTTP MCP server (FastMCP 3.0.0b1, protocol 2024-11-05). Handles the
  initialize handshake + mcp-session-id header, the mandatory
  notifications/initialized follow-up, and tools/call, extracting the
  JSON-RPC result out of the single-frame SSE response body.
- RequirementRecord: mirrors reqif-opa-mcp's requirement-record.schema.json
  exactly (uid/key/subtypes/status/policy_baseline/rubrics/text/attrs).
- requirement_record_to_node(): converts a RequirementRecord into the
  arc-kit-au Requirement struct from ledgrrr#184 (ArtifactKind::Requirement /
  NodeType::Requirement). requirement_id<-uid, title<-key, rationale<-text,
  source<-attrs.source_standard(+source_url) falling back to the policy
  baseline id, status passed through, related_decisions always empty
  (reqif-opa-mcp carries no decision links; those are created later in
  arc-kit-au itself).

Verified live end-to-end against a real reqif-opa-mcp checkout
(uv sync --extra ingest-lite; uv run python -m reqif_mcp --http --port
8123) parsing both its own sample derived baselines,
samples/standards/derived/{nist_ssdf_dogfood,owasp_asvs_cwe}.reqif (4
and 7 requirements respectively), through reqif_parse -> reqif_query ->
requirement_record_to_node -> Requirement::node_id(), all producing
correct, deterministic req: NodeIds. That live run is captured as an
#[ignore]'d integration test (tests/live_server.rs) since it needs an
external repo checkout + Python/uv, not something CI or a fresh clone
has; re-run manually with REQIF_MCP_URL set to a running server.

cargo test -p reqif-mcp-spike: 5 unit tests pass, 1 ignored (live).
cargo check --workspace --all-features: clean. cargo clippy -p
reqif-mcp-spike --all-targets: clean.
…ask 6)

- EvidenceArgs gains import_requirement/record_decision/record_cost
  actions, each constructing the corresponding arc-kit-au node
  (Requirement/Decision/Cost from ledgrrr#184) and inserting it into the
  evidence graph via EvidenceGraph::add_node. DuplicateNode is treated as
  idempotent success (content-hash dedup), matching the idempotent
  semantics already established for EvidenceBuilder's ensure_* methods.
- parse_evidence_node_type gains requirement/req, decision/dec, cost
  (plus previously-missing rnd_activity/tax_offset, same class of gap).
- Summary's node_counts and ListNodes' invalid-type error message
  extended to include the 3 new types.
- EVIDENCE_TOOL's contract.rs action list and purpose string updated.

Verified: new tests/evidence_requirement_decision_cost.rs (3 tests,
covering import+list+detail+summary, record_decision+record_cost, and
idempotent re-import) all pass; cargo build -p ledgerr-mcp --features
legacy clean (1 pre-existing unrelated warning).
Task 6 (ledgrrr#186's second commit) added import_requirement/
record_decision/record_cost to ledgerr_evidence's EVIDENCE_TOOL actions
in contract.rs but never re-ran regen-docs, so the checked-in
mcp-capability-contract.md drifted — caught by CI's check-drift step
on downstream stacked PRs (#190, #193).

Ran: cargo run -p ledgerr-mcp --bin regen-docs
…0.0)

Pre-existing drift, unrelated to Requirement/Decision/Cost content (28
objects, unchanged) -- the workspace version was bumped to 1.10.0 at
some point after this artifact was last regenerated. Only surfaced now
because check-drift's earlier mcp-capability-contract.md failure (fixed
in dd168f7) was masking this second, independent drift.

Ran: cargo run -p xtask-mcpb -- export-viz-manifest
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