spike: sysml-v2-parser round-trip against holon-viz emitter - #187
Closed
elasticdotventures wants to merge 2 commits into
Closed
spike: sysml-v2-parser round-trip against holon-viz emitter#187elasticdotventures wants to merge 2 commits into
elasticdotventures wants to merge 2 commits into
Conversation
Adds crates/sysml-v2-parser-spike, a throwaway investigation binary + integration tests that feed holon-viz's SysmlV2Emitter output and OMG sample corpora (RootPackageTest.sysml, PartTest.sysml, Don't Panic Batmobile) through the sysml-v2-parser crate. Findings (full write-up in docs/sysml-v2-parser-spike.md): - holon-viz's SysmlV2Emitter output does not currently parse as valid SysML v2, for two independent reasons: (1) its emitted closing brace sits inside a // line comment and never actually closes the block body, and (2) it emits 'block def' (SysML v1 terminology) instead of SysML v2's 'part def'. - The parser handles the smallest OMG sample (RootPackageTest.sysml) with zero diagnostics, but hits real gaps on more complex constructs (PartTest.sysml, Batmobile) that line up with the crate's own open GitHub issues. - parse_for_editor() never panics across malformed/garbage/empty input and returns structured diagnostics with actionable suggestions - resilient editor mode holds up under adversarial input. Recommendation: adopt-with-caveats. See doc for details.
elasticdotventures
added a commit
that referenced
this pull request
Aug 23, 2026
…sion/Cost (#184) * feat: widen ArtifactKind/NodeType/OperationKind with Requirement/Decision/Cost Task 3 from docs/systems-modeling-registry-rescope.md (epic part 2, ledgrrr#181), backed by sysml-derive (#183) per the resolved LinkML comparison (#3a). arc-kit-au: - NodeType gains Requirement/Decision/Cost variants (prefixes req/dec/cost) - New Requirement/Decision/Cost structs, following the existing Transaction/Classification pattern (content-hashed node_id()), each annotated with #[derive(SysmlBlock)] from sysml-derive - EvidenceNode gains matching variants; node_id()/node_type()/tx_id() updated (tx_id() -> None for all three, same as SourceDoc/ExtractedRow) - arc-kit-au now depends on sysml-derive ledger-core: - ArtifactKind gains matching variants + canonical_name() arms - arc_kit_bridge's From<ArtifactKind> for NodeType maps them explicitly (was previously falling through the _ => Unknown wildcard) - OperationKind gains RecordDecision/RecordCost/ImportRequirement, each with a corresponding LedgerOperation impl (content-hashed, idempotent, same shape as CheckTaxDeadlineOp) and wired into OperationDispatcher::from_scheduled_events ledgerr-mcp: - evidence_node_type_label() (behind the legacy feature) gets the 3 new NodeType arms it was missing Verified: cargo test -p arc-kit-au (46 tests), cargo test -p ledger-core --lib (184 tests), cargo build -p ledgerr-mcp --features legacy, and cargo check --workspace --all-features all pass. clippy clean on the touched files. Retrofitting existing variants (Transaction/TaxCategory/etc.) onto sysml-derive is a tracked follow-on, not done here, per the doc's own scoping. * fix(sysml-derive): map primitive scalars + DateTime to SysML ScalarValues DateTime<Utc> previously emitted the literal, invalid text `DateTime<Utc>` as a SysML v2 attribute type -- SysML v2's grammar has no angle-bracket generic-parameter syntax, so this would fail to parse under any conformant SysML v2 tool. bool/usize/etc. also passed through as bare Rust keywords with no corresponding SysML type. Map DateTime<_> -> ScalarValues::String, bool -> ScalarValues::Boolean, unsigned ints -> ScalarValues::Natural, signed ints -> ScalarValues::Integer, floats -> ScalarValues::Rational. Any other single-type-argument generic is now a compile error instead of a silent invalid-syntax emission. Opaque domain types (NodeId, Confidence, Decimal) still pass through as bare names -- documented as an intentional modeling assumption, not a bug. Adds regression tests for the field types introduced by #184/#193 (Decimal, Confidence, bool, usize, DateTime<Utc>) that basic.rs never exercised. Closes ledgrrr#195. * fix(holon-viz): emit valid SysML v2 (part def, not block def); wire real parser validation into ufo-types Ran SysmlV2Emitter's own output through the real sysml-v2-parser crate (docs/sysml-v2-parser-spike.md, an existing unmerged spike) and confirmed two bugs make it non-parseable: - The closing '}' was on the same line as a trailing '//' comment, so the comment swallowed it -- the block was never syntactically closed. - The emitter used SysML v1's 'block def' keyword. SysML v2 renamed this construct to 'part def'; 'block' is not a SysML v2 keyword at all. Fixed both. Added ufo_types::sysml -- a shared Constraint/Satisfies-based SysML v2 syntax validator wired to sysml-v2-parser (pinned to =0.54.0 per the spike's crate-health findings; not wasm32-compatible, so this must stay out of holon-viz's runtime dependency graph -- added to holon-viz only as a dev-dependency, used in a new round-trip test that feeds the emitter's own output through the real parser instead of just asserting on substrings. This is the concrete round-trip-closed signal the existing spike (PR #187) called out as the next step. * fix(sysml-derive): emit part def (not block def); validate against real SysML v2 grammar Same bug holon-viz's SysmlV2Emitter had (ledgrrr#197): SysML v1 called this construct Block/'block def'; SysML v2 renamed it to 'part def', and 'block' is not a SysML v2 keyword at all. Confirmed via the newly-wired ufo_types::sysml::validate_sysml_v2 (real sysml-v2-parser crate, not a hand-rolled heuristic) that the fixed output actually parses. Adds tests/real_grammar_validation.rs: runs the actual generated sysml_block_def() text for Transaction/Requirement/ExtractedRow/ ModelProposal/WorkbookRow (mirroring the real production structs from #184/#193) through the real parser. This replaces the 'no angle brackets' manual check used to validate the earlier DateTime/bool/usize scalar mapping fix with genuine grammar validation, closing out ledgrrr#195. * test(arc-kit-au): update block def -> part def assertion (sysml-derive fix) * chore: regenerate viz-manifest.json (version drift, ledgrrr#194) * feat(iso): dedicated ZLayer::SystemsModel variant (#185) * feat(iso): add dedicated ZLayer::SystemsModel variant Requirement/Decision/Cost content (ledgrrr#184) gets its own isometric layer rather than folding into the existing 6 or the still-unimplemented proposed Domain layer (docs/ontological-implementation-spec.md §6.1), per decision 2 in docs/systems-modeling-registry-rescope.md: independent toggle/color in the renderer over reusing an ontological-concepts layer. index=6, base_z=816.0 (continuing the existing 136.0 spacing), color #be185d (distinct from all 6 existing hexes). No HasVisualization impls wired yet for Requirement/Decision/Cost themselves — that requires also touching xtask's viz_manifest export + the checked-in viz-manifest.json per iso_objects.rs's own convention, tracked as a follow-on alongside task 6 (ledgerr-mcp/contract.rs wiring), not done here. * feat: spike reqif-opa-mcp over MCP + Requirement converter (#186) * feat(reqif-mcp-spike): Rust MCP client for reqif-opa-mcp + Requirement 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. * feat(ledgerr-mcp): wire Requirement/Decision/Cost into contract.rs (task 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). * docs: regenerate mcp-capability-contract.md (drift from task 6) 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 * docs: regenerate viz-manifest.json (stale version field, 1.9.0 -> 1.10.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 * feat(iso): HasVisualization + viz_manifest wiring for Requirement/Decision/Cost (#190) * feat(iso): HasVisualization + viz_manifest wiring for Requirement/Decision/Cost Adds dedicated SemanticType::{Requirement,Decision,Cost} variants (iso.rs) and HasVisualization impls for arc_kit_au::node::{Requirement, Decision,Cost} (iso_objects.rs, gated behind the arc-kit-au feature, matching ontology.rs's arc_kit_bridge precedent), all routed to ZLayer::SystemsModel (added in ledgrrr#185). Wires the 3 new types into xtask's export_viz_manifest (now 31 domain types, up from 28) and regenerates the checked-in viz-manifest.json. Updates pipeline_e2e.rs's EXPECTED_ENTRY_COUNT and representative-type assertions to match. Verified: cargo test -p ledger-core --lib (185 passed), cargo test -p ledgerr-mcp --test pipeline_e2e (manifest count test passes), cargo check --workspace --all-features clean. This closes out the one remaining gap from ledgrrr#185 (task 4 in docs/systems-modeling-registry-rescope.md's §6), left explicitly unfinished there pending this larger change. * feat(arc-kit-au): retrofit #[derive(SysmlBlock)] onto existing node types (#193) Applies the sysml-derive macro (feat/sysml-derive-spike, #183) to the 8 pre-existing arc-kit-au node structs that predate the systems-modeling epic: SourceDoc, ExtractedRow, Transaction, Classification, ModelProposal, OperatorApproval, ValidationIssue, WorkbookRow. This was the second of two explicitly-deferred follow-ons from task 3 (#184) — the first (HasVisualization/viz_manifest wiring for Requirement/Decision/Cost) landed as part of this same stack. The derive is purely syntactic (walks named fields via syn, stringifies each field's type via quote!) so it applies uniformly regardless of field type — no per-struct special-casing needed, confirmed by re-reading crates/sysml-derive/src/lib.rs before applying. Verified: cargo test -p sysml-derive (2 tests), cargo test -p arc-kit-au (46 tests, unchanged from pre-retrofit baseline), cargo check --workspace --all-features (clean, one pre-existing unrelated warning in ledgerr-mcp/src/fbar.rs), cargo clippy -p arc-kit-au --all-features (clean).
elasticdotventures
added a commit
that referenced
this pull request
Aug 23, 2026
* feat(sysml-derive): spike a Rust-AST-to-SysML-v2 derive macro New crate: #[derive(SysmlBlock)] walks a struct's named fields via syn and generates a sysml_block_def() associated fn returning the equivalent SysML-v2 'block def' text at compile time (matching holon-viz's SysmlV2Emitter 'block def' terminology). Field-type mapping: Vec<T> -> T[*], Option<T> -> T[0..1], everything else -> plain T. Not yet checked against a real SysML-v2 grammar/parser (Part 1's Tier 0 candidates) -- this is the spike from docs/systems-modeling-registry-rescope.md #2a/#6 task 1, proving the AST-walk direction before comparing it against the LinkML spike (task 2) and wiring either into the real ArtifactKind/NodeType widening (task 3). Tests mirror arc-kit-au::Transaction's and ::Classification's field shapes (name+type only, no dependency on arc-kit-au itself -- wiring the derive onto the real production structs is task 3, not this spike) and assert on the emitted block def text for both the Vec and Option multiplicity branches. Same technique this codebase already approved for a different target: AGENTS.md (PM-3, 2026-05-13) sanctions #[derive(specta::Type)] for Rust-to-TypeScript; this is Rust-to-SysML-v2/KerML instead. * chore: regenerate viz-manifest.json (version drift 1.9.0 -> 1.10.0) * feat: widen ArtifactKind/NodeType/OperationKind with Requirement/Decision/Cost (#184) * feat: widen ArtifactKind/NodeType/OperationKind with Requirement/Decision/Cost Task 3 from docs/systems-modeling-registry-rescope.md (epic part 2, ledgrrr#181), backed by sysml-derive (#183) per the resolved LinkML comparison (#3a). arc-kit-au: - NodeType gains Requirement/Decision/Cost variants (prefixes req/dec/cost) - New Requirement/Decision/Cost structs, following the existing Transaction/Classification pattern (content-hashed node_id()), each annotated with #[derive(SysmlBlock)] from sysml-derive - EvidenceNode gains matching variants; node_id()/node_type()/tx_id() updated (tx_id() -> None for all three, same as SourceDoc/ExtractedRow) - arc-kit-au now depends on sysml-derive ledger-core: - ArtifactKind gains matching variants + canonical_name() arms - arc_kit_bridge's From<ArtifactKind> for NodeType maps them explicitly (was previously falling through the _ => Unknown wildcard) - OperationKind gains RecordDecision/RecordCost/ImportRequirement, each with a corresponding LedgerOperation impl (content-hashed, idempotent, same shape as CheckTaxDeadlineOp) and wired into OperationDispatcher::from_scheduled_events ledgerr-mcp: - evidence_node_type_label() (behind the legacy feature) gets the 3 new NodeType arms it was missing Verified: cargo test -p arc-kit-au (46 tests), cargo test -p ledger-core --lib (184 tests), cargo build -p ledgerr-mcp --features legacy, and cargo check --workspace --all-features all pass. clippy clean on the touched files. Retrofitting existing variants (Transaction/TaxCategory/etc.) onto sysml-derive is a tracked follow-on, not done here, per the doc's own scoping. * fix(sysml-derive): map primitive scalars + DateTime to SysML ScalarValues DateTime<Utc> previously emitted the literal, invalid text `DateTime<Utc>` as a SysML v2 attribute type -- SysML v2's grammar has no angle-bracket generic-parameter syntax, so this would fail to parse under any conformant SysML v2 tool. bool/usize/etc. also passed through as bare Rust keywords with no corresponding SysML type. Map DateTime<_> -> ScalarValues::String, bool -> ScalarValues::Boolean, unsigned ints -> ScalarValues::Natural, signed ints -> ScalarValues::Integer, floats -> ScalarValues::Rational. Any other single-type-argument generic is now a compile error instead of a silent invalid-syntax emission. Opaque domain types (NodeId, Confidence, Decimal) still pass through as bare names -- documented as an intentional modeling assumption, not a bug. Adds regression tests for the field types introduced by #184/#193 (Decimal, Confidence, bool, usize, DateTime<Utc>) that basic.rs never exercised. Closes ledgrrr#195. * fix(holon-viz): emit valid SysML v2 (part def, not block def); wire real parser validation into ufo-types Ran SysmlV2Emitter's own output through the real sysml-v2-parser crate (docs/sysml-v2-parser-spike.md, an existing unmerged spike) and confirmed two bugs make it non-parseable: - The closing '}' was on the same line as a trailing '//' comment, so the comment swallowed it -- the block was never syntactically closed. - The emitter used SysML v1's 'block def' keyword. SysML v2 renamed this construct to 'part def'; 'block' is not a SysML v2 keyword at all. Fixed both. Added ufo_types::sysml -- a shared Constraint/Satisfies-based SysML v2 syntax validator wired to sysml-v2-parser (pinned to =0.54.0 per the spike's crate-health findings; not wasm32-compatible, so this must stay out of holon-viz's runtime dependency graph -- added to holon-viz only as a dev-dependency, used in a new round-trip test that feeds the emitter's own output through the real parser instead of just asserting on substrings. This is the concrete round-trip-closed signal the existing spike (PR #187) called out as the next step. * fix(sysml-derive): emit part def (not block def); validate against real SysML v2 grammar Same bug holon-viz's SysmlV2Emitter had (ledgrrr#197): SysML v1 called this construct Block/'block def'; SysML v2 renamed it to 'part def', and 'block' is not a SysML v2 keyword at all. Confirmed via the newly-wired ufo_types::sysml::validate_sysml_v2 (real sysml-v2-parser crate, not a hand-rolled heuristic) that the fixed output actually parses. Adds tests/real_grammar_validation.rs: runs the actual generated sysml_block_def() text for Transaction/Requirement/ExtractedRow/ ModelProposal/WorkbookRow (mirroring the real production structs from #184/#193) through the real parser. This replaces the 'no angle brackets' manual check used to validate the earlier DateTime/bool/usize scalar mapping fix with genuine grammar validation, closing out ledgrrr#195. * test(arc-kit-au): update block def -> part def assertion (sysml-derive fix) * chore: regenerate viz-manifest.json (version drift, ledgrrr#194) * feat(iso): dedicated ZLayer::SystemsModel variant (#185) * feat(iso): add dedicated ZLayer::SystemsModel variant Requirement/Decision/Cost content (ledgrrr#184) gets its own isometric layer rather than folding into the existing 6 or the still-unimplemented proposed Domain layer (docs/ontological-implementation-spec.md §6.1), per decision 2 in docs/systems-modeling-registry-rescope.md: independent toggle/color in the renderer over reusing an ontological-concepts layer. index=6, base_z=816.0 (continuing the existing 136.0 spacing), color #be185d (distinct from all 6 existing hexes). No HasVisualization impls wired yet for Requirement/Decision/Cost themselves — that requires also touching xtask's viz_manifest export + the checked-in viz-manifest.json per iso_objects.rs's own convention, tracked as a follow-on alongside task 6 (ledgerr-mcp/contract.rs wiring), not done here. * feat: spike reqif-opa-mcp over MCP + Requirement converter (#186) * feat(reqif-mcp-spike): Rust MCP client for reqif-opa-mcp + Requirement 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. * feat(ledgerr-mcp): wire Requirement/Decision/Cost into contract.rs (task 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). * docs: regenerate mcp-capability-contract.md (drift from task 6) 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 * docs: regenerate viz-manifest.json (stale version field, 1.9.0 -> 1.10.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 * feat(iso): HasVisualization + viz_manifest wiring for Requirement/Decision/Cost (#190) * feat(iso): HasVisualization + viz_manifest wiring for Requirement/Decision/Cost Adds dedicated SemanticType::{Requirement,Decision,Cost} variants (iso.rs) and HasVisualization impls for arc_kit_au::node::{Requirement, Decision,Cost} (iso_objects.rs, gated behind the arc-kit-au feature, matching ontology.rs's arc_kit_bridge precedent), all routed to ZLayer::SystemsModel (added in ledgrrr#185). Wires the 3 new types into xtask's export_viz_manifest (now 31 domain types, up from 28) and regenerates the checked-in viz-manifest.json. Updates pipeline_e2e.rs's EXPECTED_ENTRY_COUNT and representative-type assertions to match. Verified: cargo test -p ledger-core --lib (185 passed), cargo test -p ledgerr-mcp --test pipeline_e2e (manifest count test passes), cargo check --workspace --all-features clean. This closes out the one remaining gap from ledgrrr#185 (task 4 in docs/systems-modeling-registry-rescope.md's §6), left explicitly unfinished there pending this larger change. * feat(arc-kit-au): retrofit #[derive(SysmlBlock)] onto existing node types (#193) Applies the sysml-derive macro (feat/sysml-derive-spike, #183) to the 8 pre-existing arc-kit-au node structs that predate the systems-modeling epic: SourceDoc, ExtractedRow, Transaction, Classification, ModelProposal, OperatorApproval, ValidationIssue, WorkbookRow. This was the second of two explicitly-deferred follow-ons from task 3 (#184) — the first (HasVisualization/viz_manifest wiring for Requirement/Decision/Cost) landed as part of this same stack. The derive is purely syntactic (walks named fields via syn, stringifies each field's type via quote!) so it applies uniformly regardless of field type — no per-struct special-casing needed, confirmed by re-reading crates/sysml-derive/src/lib.rs before applying. Verified: cargo test -p sysml-derive (2 tests), cargo test -p arc-kit-au (46 tests, unchanged from pre-retrofit baseline), cargo check --workspace --all-features (clean, one pre-existing unrelated warning in ledgerr-mcp/src/fbar.rs), cargo clippy -p arc-kit-au --all-features (clean).
elasticdotventures
added a commit
that referenced
this pull request
Aug 23, 2026
…eal parser validation into ufo-types Ran SysmlV2Emitter's own output through the real sysml-v2-parser crate (docs/sysml-v2-parser-spike.md, an existing unmerged spike) and confirmed two bugs make it non-parseable: - The closing '}' was on the same line as a trailing '//' comment, so the comment swallowed it -- the block was never syntactically closed. - The emitter used SysML v1's 'block def' keyword. SysML v2 renamed this construct to 'part def'; 'block' is not a SysML v2 keyword at all. Fixed both. Added ufo_types::sysml -- a shared Constraint/Satisfies-based SysML v2 syntax validator wired to sysml-v2-parser (pinned to =0.54.0 per the spike's crate-health findings; not wasm32-compatible, so this must stay out of holon-viz's runtime dependency graph -- added to holon-viz only as a dev-dependency, used in a new round-trip test that feeds the emitter's own output through the real parser instead of just asserting on substrings. This is the concrete round-trip-closed signal the existing spike (PR #187) called out as the next step.
elasticdotventures
added a commit
that referenced
this pull request
Aug 23, 2026
…eal parser validation into ufo-types (#197) Ran SysmlV2Emitter's own output through the real sysml-v2-parser crate (docs/sysml-v2-parser-spike.md, an existing unmerged spike) and confirmed two bugs make it non-parseable: - The closing '}' was on the same line as a trailing '//' comment, so the comment swallowed it -- the block was never syntactically closed. - The emitter used SysML v1's 'block def' keyword. SysML v2 renamed this construct to 'part def'; 'block' is not a SysML v2 keyword at all. Fixed both. Added ufo_types::sysml -- a shared Constraint/Satisfies-based SysML v2 syntax validator wired to sysml-v2-parser (pinned to =0.54.0 per the spike's crate-health findings; not wasm32-compatible, so this must stay out of holon-viz's runtime dependency graph -- added to holon-viz only as a dev-dependency, used in a new round-trip test that feeds the emitter's own output through the real parser instead of just asserting on substrings. This is the concrete round-trip-closed signal the existing spike (PR #187) called out as the next step.
elasticdotventures
pushed a commit
that referenced
this pull request
Aug 30, 2026
PR #187 (spike/sysml-v2-parser-roundtrip) found holon-viz's SysmlV2Emitter produced invalid SysML v2 (comment-swallowed closing brace, `block def` instead of `part def`). Both bugs were independently fixed on main in PR #183 (c106a5e) before this doc landed. Cherry-picks only the investigation doc; the throwaway sysml-v2-parser-spike crate stays on the closed PR's branch since its regression test now asserts stale (pre-fix) behavior.
Member
Author
|
Findings ported: the two holon-viz SysmlV2Emitter bugs this spike found (comment-swallowed closing brace, |
elasticdotventures
pushed a commit
that referenced
this pull request
Aug 30, 2026
PR #187 (spike/sysml-v2-parser-roundtrip) found holon-viz's SysmlV2Emitter produced invalid SysML v2 (comment-swallowed closing brace, `block def` instead of `part def`). Both bugs were independently fixed on main in PR #183 (c106a5e) before this doc landed. Cherry-picks only the investigation doc; the throwaway sysml-v2-parser-spike crate stays on the closed PR's branch since its regression test now asserts stale (pre-fix) behavior.
elasticdotventures
added a commit
that referenced
this pull request
Aug 31, 2026
…tings model (#216) * fix(ledgrrr-settings): stop leaking test data; replace BurntToast/PowerShell; retire host-tray.rs - NotificationBackend::PowerShell renamed to Native (serde alias keeps reading settings persisted under the old name) — the actual toast delivery is being replaced with native windows-rs calls in ledgerr-host, this crate only owns the shared data types. - SettingsStore::with_backend(path, backend) added alongside new(), so tests can construct an isolated JsonFileBackend directly instead of going through Windows registry auto-selection — new()'s registry backend ignores its path argument entirely and always targets one fixed production key, which is correct for real callers but means any test using new() over a tempdir shares that one global mutable key with every other test and any real running instance. - crates/ledgerr-host/src/bin/host-tray.rs moved to src/bin/legacy/ as reference-only (git mv staged this alongside the above; it's the first step of retiring the standalone host-tray.exe binary — completed in the following commits, which remove its [[bin]] entry and repoint host-tauri's tray at the same shared runtime it used). * fix(ledgerr-desktop-agent): stop leaking test registry keys; drop stale host-tray refs settings_server.rs's tests hit the same registry-auto-selection issue fixed in ledgrrr-settings — repoint them at SettingsStore::with_backend + JsonFileBackend explicitly. status.rs/service_control.rs still listed host-tray as a tray-binary candidate; it's being retired in favor of host-tauri.exe, which already sorts first in that list. * feat(notify): replace BurntToast/PowerShell dependency with native windows-rs toasts The tray's toast notifications shelled out to powershell.exe running Import-Module BurntToast; New-BurntToastNotification — requiring a separately-installed PowerShell module on every machine. A native windows::UI::Notifications-based ToastNotifier already existed in this crate (notification::windows_toast) but was never wired into the tray's actual notification path — only its own unused tests exercised it. Added notify::native::NativeToastNotifier, implementing the same notify::types::Notifier trait the tray already consumes, wrapping that existing native implementation (and the stderr fallback on non-Windows) instead of shelling out to any external process. * refactor(tray): remove dead code, group notification toggles into a submenu native.rs: drop the unused push_check helper, DYNAMIC_TEXT_IDS and CHECK_ITEM_IDS constants, and fix a few compiler-flagged unused-mut/ unused-must-use warnings. Group the four notification-event toggles (approval/submitted/failed/ completed) into a "Notify me for" submenu instead of sitting flat among the other 10 top-level menu items — cuts the flat menu from 14 items to 11 and keeps the related settings together. * refactor(tray): make ShowWindow's action injectable; harden against a down settings service Ports this session's earlier work (originally built against a SettingsStore this branch predates) onto the client/server settings architecture already on main (SettingsClient, ledgrrr-service): - TrayCommand::ShowWindow's action is now an injected closure instead of a hardcoded spawn of a separate process, so the same run()/ handle_command can be reused by host-tauri (which shows its own webview) without any dependency on a standalone binary. - Collapsed 7 near-identical toggle handlers into negate()/ apply_toggle() helpers. - Any single command's error (most realistically: ledgrrr-service isn't running yet) is now logged and the loop continues, instead of propagating via `?` out of run() and killing the whole tray — and, via host-tauri's exit-on-return, the whole app — over a transient failure. Only an explicit Quit ends the loop. - run()'s own initial settings load falls back to defaults on failure instead of erroring out before ever creating the tray icon at all — confirmed live: with no settings service running, the tray now still appears (previously it silently never initialized). - NotificationBackend::PowerShell -> Native throughout. * feat(tray): give host-tauri's Windows tray full parity with the old host-tray.exe Replaces the minimal Show/Exit-only stub with the shared tray::runtime::run(), injecting "show/unminimize/focus the Tauri webview" as the window-show action. host-tauri now has the full toggle/notification/toast menu that was previously only in the standalone host-tray.exe, fixing the toast-notifications-never-wired-up bug on the main app's tray. Also: main.rs's window-visibility check and enable_tray check both silently fell back to unhelpful defaults (or, for enable_tray, silently skipped tray setup entirely) whenever the settings load failed — exactly the case when ledgrrr-service isn't running yet. Both now log and use an explicit, sensible default (visible; tray enabled) instead. * chore(tray): retire host-tray.exe as a build target Removes its [[bin]] entry now that host-tauri.exe has full feature parity (previous commit). Source stays at src/bin/legacy/host-tray.rs for reference; it no longer compiles as part of any normal build. * chore(justfile): repoint host-tray recipes at host-tauri host-tray no longer exists as a build target (previous commit). * docs: port sysml-v2-parser spike findings from PR #187 PR #187 (spike/sysml-v2-parser-roundtrip) found holon-viz's SysmlV2Emitter produced invalid SysML v2 (comment-swallowed closing brace, `block def` instead of `part def`). Both bugs were independently fixed on main in PR #183 (c106a5e) before this doc landed. Cherry-picks only the investigation doc; the throwaway sysml-v2-parser-spike crate stays on the closed PR's branch since its regression test now asserts stale (pre-fix) behavior. * feat(status): add FoundryLocalStatus detection to LedgrrrStatus * feat(foundry_install_plan): add winget install-assist plan-before-mutation flow * feat(tauri): wire Foundry Local install-plan/action commands Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015kUr7Kf9wN15TPqDiKsiKw * docs(foundry_install_plan): fix misleading doc comment (Foundry Local, not this repo's own MSIX) * feat(lifecycle): add FoundryLocalLifecycle enum and status derivation * feat(lifecycle): export FoundryLocalLifecycle as an SCXML statechart * fix(ledgerr-host): close registry-key test-isolation gap in 3 test files This PR's own with_backend()/JsonFileBackend fix for the registry-leak issue (SettingsStore::new's Windows registry backend ignores its path arg and always targets one fixed production key) landed in ledgrrr-settings and ledgerr-desktop-agent, but missed 3 test files in ledgerr-host itself — the crate that actually owns the tray's real SettingsStore usage: - tests/settings_roundtrip.rs (5 tests) - tests/settings_atomicity.rs (2 tests) - tests/tray_wiring_smoke.rs (tray_enable_roundtrips_through_store) On a real Windows box these all still called SettingsStore::new(tempdir) directly, silently opening the one production registry key instead of an isolated per-test backend — the exact corruption/cross-contamination bug this PR claims to have fixed elsewhere, and in the worst case (tray_enable_roundtrips_through_store) capable of writing enable_tray: false into a real running host-tauri.exe's live settings from `cargo test`. CI never caught it: the only Windows job runs a packaging smoke test, not `cargo test`, and on Linux the registry backend is compiled out entirely so these pass cleanly via the (correctly path-scoped) JsonFileBackend fallback regardless. Applied the same with_backend(path, JsonFileBackend::new(path)) pattern already used in ledgrrr-settings/src/store.rs and ledgerr-desktop-agent/src/settings_server.rs. Verified: cargo test -p ledgerr-host --tests — 112 passed, 0 failed. * docs: add Phase 4 and Phase 5 implementation plans * fix: address ledgrrr#216 review findings - Restore docs/superpowers/specs/2026-08-29-tray-tauri-integration-design.md and its companion plan doc from the superseded feat/windows-desktop-dogfood branch — Justfile referenced the spec doc, but it was never ported when this branch superseded that one. Retrieved verbatim, status line updated to note the branch handoff. - Add invoke_reports_blocked_on_non_windows_even_when_approved, gated #[cfg(not(windows))] — exercises the untested !plan.executable_now branch in foundry_install_plan.rs::invoke() on non-Windows CI targets. Deliberately excluded from Windows builds: on Windows, executable_now is always true, so calling invoke(approved: true) there would reach the real winget-spawning code path as a side effect of running the test suite — the existing invoke_without_approval_never_launches test already covers the approval gate safely on every target. --------- Co-authored-by: brianh <brianh@promptexecution.com> 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
sysml-v2-parsercrate (crates.io) — flagged as the top Tier-0 candidate in the SysML v2 tooling survey (docs: SysML v2 tooling survey (epic part 1) #180) — against real SysML-v2 text: holon-viz's ownSysmlV2Emitteroutput plus OMG sample corpora (RootPackageTest.sysml,PartTest.sysml, "Don't Panic" Batmobile).crates/sysml-v2-parser-spike(throwaway investigation binary + 3 pinned integration tests, not production code).docs/sysml-v2-parser-spike.md.Key findings
SysmlV2Emitteroutput does not currently parse as valid SysML v2 — two independent bugs: (1) the emitted closing}sits inside a//line comment and never actually closes the block body, and (2) it emitsblock def(SysML v1 terminology) instead of SysML v2'spart def. Filed as a finding here, not fixed in this PR (out of scope for a read-only spike).RootPackageTest.sysml) with zero diagnostics, but hits real gaps on more complex constructs (PartTest.sysml, Batmobile) that line up almost 1:1 with the crate's own open GitHub issues.parse_for_editor()never panics across malformed/garbage/empty input and returns structured, actionable diagnostics — "resilient editor mode" holds up under adversarial input, not just the happy path.Recommendation
Adopt-with-caveats. See doc for full reasoning and recommended next steps (fix holon-viz's emitter bugs first, pin exact version, budget for upstream issue triage).
Test plan
cargo build -p sysml-v2-parser-spikecargo test -p sysml-v2-parser-spike(3/3 pass)cargo build --workspace(unaffected, builds clean)