Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
9567186
fix(tooling): reconcile decision traceability with owned documentation
MrScripty Oct 2, 2026
e22ebb2
fix(ci): select explicit fork-point ranges for pull requests
MrScripty Oct 2, 2026
4cf2317
fix(ci): provision native prerequisites and pin C# generator
MrScripty Oct 2, 2026
74723b5
test(workflow): restore canonical contract fixture preconditions
MrScripty Oct 2, 2026
333e53d
fix(workflow): preserve session capacity state and diagnostics
MrScripty Oct 3, 2026
6a5b134
test(workflow): align headless contracts with scheduler execution
MrScripty Oct 3, 2026
e1bf2d8
test(workflow): guard required legacy host compatibility hook
MrScripty Oct 3, 2026
2c5f22b
fix(beam): align smoke stubs with default native exports
MrScripty Oct 3, 2026
7b6dc58
test(beam): preserve canonical graph validation and selector contracts
MrScripty Oct 3, 2026
8ac413c
test(http): exercise transport contracts with bounded server waits
MrScripty Oct 3, 2026
f4fec1f
test(uniffi): assert canonical typed memory estimate
MrScripty Oct 3, 2026
ebbffc8
test(uniffi): preserve canonical model selector identity
MrScripty Oct 3, 2026
33018ef
test(uniffi): report missing managed runtime from actual fixture evid…
MrScripty Oct 3, 2026
8a8dcbc
fix(uniffi): publish owner-derived validation before native execution
MrScripty Oct 3, 2026
b7b4da4
test(workflow): publish real validation in text attribution fixtures
MrScripty Oct 3, 2026
2104574
fix(ui): derive model selector state without synchronization effect
MrScripty Oct 3, 2026
8888878
fix(ci): parse Svelte role-button attributes structurally
MrScripty Oct 3, 2026
e5e2bdf
fix(ui): own artifact download anchors and URL release lifecycle
MrScripty Oct 3, 2026
fef75dd
fix(scheduler): implement standard AsRef for validated contracts
MrScripty Oct 3, 2026
8a08b6a
fix(ci): remove redundant path borrow and Result annotation
MrScripty Oct 3, 2026
e6160c0
fix(a11y): disclose video caption limits and document graph activation
MrScripty Oct 3, 2026
0b5c0df
fix(deps): update frontend security dependency cohort
MrScripty Oct 3, 2026
f6d03a0
ci: qualify frontend dependency tree and build
MrScripty Oct 3, 2026
7e0dd20
fix(scheduler): box runtime execution intent payload
MrScripty Oct 3, 2026
5218c1a
fix(media): accept named conversion result input
MrScripty Oct 3, 2026
cac290b
fix(inference): preserve behavior while clearing annotation and style…
MrScripty Oct 3, 2026
58df816
fix(inference): box managed diagnostic and planned image payloads
MrScripty Oct 3, 2026
1cae42e
fix(ci): execute feature-gated PyTorch projection tests
MrScripty Oct 3, 2026
4bbf55e
refactor(inference): group private gateway lifecycle contexts
MrScripty Oct 3, 2026
23c0c14
fix(inference): box two concrete error return boundaries
MrScripty Oct 3, 2026
fb61592
refactor(inference): name PyTorch text generation inputs
MrScripty Oct 3, 2026
9458fac
refactor(inference): reuse effective llama runtime settings
MrScripty Oct 3, 2026
c277a0b
docs(inference): clarify default-deny remote code policy
MrScripty Oct 3, 2026
92ca205
refactor(node-engine): group validation lifecycle attribution
MrScripty Oct 3, 2026
a6fa075
test(node-engine): disambiguate mock backend result type
MrScripty Oct 3, 2026
3c4dbbd
refactor(runtime-host): implement standard borrowed accessors
MrScripty Oct 3, 2026
395596f
refactor(diagnostics): reuse projection state input
MrScripty Oct 3, 2026
4433482
refactor(diagnostics): box inference event payload
MrScripty Oct 3, 2026
3d8de84
refactor(diagnostics): box artifact event payload
MrScripty Oct 3, 2026
89ffaef
refactor(workflow): box private no-selection error payload
MrScripty Oct 3, 2026
25de27e
refactor(workflow): preserve contracts while clearing style lints
MrScripty Oct 3, 2026
b4637a4
refactor(workflow): box private request and completion payloads
MrScripty Oct 3, 2026
09ae318
refactor(workflow): box ready inference projection payload
MrScripty Oct 3, 2026
0c08539
refactor(workflow): group terminal diagnostic inputs
MrScripty Oct 3, 2026
48b728c
refactor(workflow): group private session run context
MrScripty Oct 3, 2026
533a80b
refactor(workflow): clarify private projection and summary types
MrScripty Oct 3, 2026
c369cbe
refactor(registry): box private selection diagnostic errors
MrScripty Oct 3, 2026
2a4d5c8
style(rust): apply pinned formatter to repair milestones
MrScripty Oct 3, 2026
c023cec
refactor(embedded): preserve defaults and routing while clearing styl…
MrScripty Oct 3, 2026
b73c5d1
refactor(embedded): preserve artifact error source through boxed payload
MrScripty Oct 3, 2026
788ce31
Reduce private embedded source payload sizes
MrScripty Oct 3, 2026
bbce93c
Align embedded fixtures with validation and reservation contracts
MrScripty Oct 3, 2026
badd802
Group private inventory diagnostics and reuse stream attribution
MrScripty Oct 3, 2026
e7e5277
Name owned hosted startup configuration inputs
MrScripty Oct 3, 2026
e2dbe87
Propagate owned runtime shutdown failures through UniFFI
MrScripty Oct 3, 2026
a387e75
Preserve node test contracts while clearing strict lint
MrScripty Oct 3, 2026
9a46796
Collect independent strict Clippy failures without early abort
MrScripty Oct 3, 2026
854a245
fix(ci): clear aggregate mechanical and test lint findings
MrScripty Oct 3, 2026
e47ddc8
refactor(desktop): bundle injected Tauri command state
MrScripty Oct 3, 2026
b2490ae
style(desktop): apply pinned Rust formatting to IPC fixtures
MrScripty Oct 3, 2026
60302e6
fix(test): keep SQLite column iterator within statement lifetime
MrScripty Oct 3, 2026
ad7e42b
fix(integration): compose reviewed frontend security cohort
MrScripty Oct 4, 2026
99b8fb3
fix(integration): compose reviewed artifact lifecycle and accessibility
MrScripty Oct 4, 2026
ca9edde
fix(quality): address external range and snippet findings
MrScripty Oct 4, 2026
572ccbc
fix(test): isolate shared Python fixtures in aggregate inference runs
Oct 4, 2026
827b1b2
docs: record PR44 native qualification evidence and blockers
Oct 4, 2026
9996ddf
fix(a11y): require independent name evidence for snippet renders
Oct 4, 2026
41701d6
fix(integration): compose Python fixture and static-name repairs
MrScripty Oct 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/headless-embedding-contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ jobs:
uses: Swatinem/rust-cache@v2

- name: Install C# UniFFI generator
run: cargo install uniffi-bindgen-cs --version 0.9.0 --locked
# Official v0.9.0+v0.28.3 source; this version is not on crates.io.
run: >-
cargo install uniffi-bindgen-cs
--git https://github.com/NordSecurity/uniffi-bindgen-cs
--rev 2f4880f03ed08d960ad0ee14d11cf95444eee540
--locked

- name: Run workflow service tests
run: cargo test -p pantograph-workflow-service
Expand Down
213 changes: 182 additions & 31 deletions .github/workflows/quality-gates.yml

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .github/workflows/runtime-separation-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Install Ubuntu native build dependencies
run: ./scripts/install-ubuntu-build-dependencies.sh

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@1.92.0

Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,29 @@ The format is based on Keep a Changelog.
canonicalization records.

### Changed
- Rust source API: `LlamaServer::start_sidecar_inference` and
`matches_inference_runtime` now borrow the existing `LlamaCppRuntimeSettings`
for effective device/context/thread/batch values. Model/mmproj paths, process
spawner and port override retain their existing roles and ownership.
- Rust source API: PyTorch `generate_with_top_k` and `generate_stream_with_top_k`
now accept `PyTorchTextGenerationRequest` with the same seven named inputs.
Legacy `generate`/`generate_stream` signatures and the worker wire contract
remain unchanged.
- Rust source API: `resolve_managed_binary_command` and
`resolve_task_registry_entry_from_evidence` now return boxed concrete errors.
Error variants, diagnostic JSON and Display text remain unchanged; callers
matching an owned error must dereference the box.
- Rust source API: `ManagedRuntimeCommandResolutionError::MissingRuntimeVariant.diagnostic`
and `ImageGenerationPlanningOutcome::Planned.plan` now hold boxed payloads.
Direct constructors must box their values and consuming plan callers must unbox;
tagged JSON, diagnostic fields, and error Display text are unchanged.
- Rust source API: `MediaConversionResult::try_new` now accepts one
`MediaConversionResultInput` with the same eight named fields instead of eight
positional arguments. Result fields and validation behavior are unchanged.
- Rust source API: `SchedulerTaskExecutionIntent::Runtime.task_intent` is now boxed.
Construct runtime variants with `SchedulerTaskExecutionIntent::runtime(intent)`;
direct struct-variant callers must pass `Box::new(intent)`. Serialized JSON and
borrowed `runtime_task_intent()` access remain unchanged.
- Root project `README.md` reorganized around install, usage, development, and contribution workflows.
- Documentation consolidated around current guides, accepted decisions, audits,
and active plans; superseded narration remains available in Git history.
Expand All @@ -24,3 +47,19 @@ The format is based on Keep a Changelog.

### Security
- Canonical path validation enforced at file-boundary entry points to block traversal and symlink escape paths.

### Runtime-host borrowed accessor compatibility

The seven validated runtime-host contract wrappers now implement standard `AsRef<Raw>` in place of inherent `as_ref` methods. Borrowed values and lifetimes, wrapper types, validation and `into_inner` are unchanged. Ordinary method syntax, qualified calls and function pointers continue to resolve through the standard prelude. Rust callers using `no_implicit_prelude` must explicitly import `std::convert::AsRef`. The workspace crate remains unpublished (`publish = false`).

### Diagnostic event inference payload construction

`DiagnosticEventPayload::InferenceExecutionDiagnosticObserved` now stores `Box<InferenceExecutionDiagnosticObservedPayload>`. Rust callers constructing this public variant must wrap the existing raw payload in `Box::new`; owned pattern bindings now contain a box. Tagged serialized JSON, raw payload fields and validation remain unchanged. This adds one allocation for this event variant; no measured performance improvement is claimed. The crate remains `publish = false`.

### Diagnostic event artifact payload construction

`DiagnosticEventPayload::IoArtifactObserved` now stores `Box<IoArtifactObservedPayload>`. Rust callers constructing the public variant use `Box::new`, and owned pattern bindings contain a box. Raw DTO fields, validation and tagged JSON stay unchanged; this adds one allocation for this variant without a measured performance claim. The crate remains `publish = false`.

### Workflow ready inference projection construction

`WorkflowSchedulerInferenceTaskProjection::Ready` now holds `Box<WorkflowSchedulerReadyInferenceTaskProjection>`. Rust constructors use `Box::new`, and owned pattern bindings contain a box; the raw record, equality, borrowed readers and downstream scheduler intent serialization remain unchanged. The projection enum itself has no Serde implementation. This adds one allocation for ready projections without a measured performance claim; the crate remains `publish = false`.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions bindings/beam/pantograph_native_smoke/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ The same runner is used by the required `Rustler BEAM smoke` job in
- The local `Pantograph.Native` shim defines generated NIF stubs for the full
default Rustler export surface so `:erlang.load_nif/2` can load the compiled
library before individual smoke tests call the narrower contract under test.
This includes the package-facts snapshot (arity 3), individual summary (arity 2),
and update-cursor query (arity 3) exports. Their stubs permit native loading;
this smoke harness does not claim model-library query behavior coverage.
The seven `frontend-http` exports are feature-gated and outside this default
build's export surface.
- This harness now also pins a backend-owned validation error path by
asserting that unknown edge endpoints round-trip back to BEAM as validation
strings through `workflow_validate/1`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ defmodule Pantograph.Native do
{:pumas_list_models, 1},
{:pumas_search_models, 4},
{:pumas_get_model, 2},
{:pumas_model_package_facts_summary_snapshot, 3},
{:pumas_resolve_model_package_facts_summary, 2},
{:pumas_list_model_library_updates_since, 3},
{:pumas_rebuild_index, 1},
{:pumas_search_hf, 4},
{:pumas_get_repo_files, 2},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ defmodule PantographNativeSmokeTest do
test "workflow_validate returns edge-reference errors for unknown nodes" do
graph_json =
Pantograph.Native.workflow_new("beam-invalid", "BEAM Invalid")
|> Pantograph.Native.workflow_add_edge("missing-source", "out", "missing-target", "in")
|> Pantograph.Native.workflow_add_node("known-source", "text-input", 0.0, 0.0, "{}")
|> Pantograph.Native.workflow_add_node("known-target", "text-output", 100.0, 0.0, "{}")
|> Pantograph.Native.workflow_add_edge("missing-source", "text", "known-target", "text")
|> Pantograph.Native.workflow_add_edge("known-source", "text", "missing-target", "text")

errors = Pantograph.Native.workflow_validate(graph_json)

assert is_list(errors)
assert Enum.any?(errors, &String.contains?(&1, "unknown node 'missing-source'"))
assert Enum.any?(errors, &String.contains?(&1, "unknown node 'missing-target'"))
assert Enum.any?(errors, &String.contains?(&1, "unknown source node 'missing-source'"))
assert Enum.any?(errors, &String.contains?(&1, "unknown target node 'missing-target'"))
end

test "workflow_from_json returns parse errors as BEAM tuples" do
Expand Down Expand Up @@ -57,6 +60,7 @@ defmodule PantographNativeSmokeTest do
queryable = Pantograph.Native.node_registry_queryable_ports(registry)

assert String.contains?(queryable, ~s("node_type":"puma-lib"))
assert String.contains?(queryable, ~s("port_id":"model_path"))
assert String.contains?(queryable, ~s("port_id":"pumas_model_ref"))
refute String.contains?(queryable, ~s("port_id":"model_path"))
end
end
51 changes: 51 additions & 0 deletions bindings/csharp/Pantograph.DirectRuntimeQuickstart/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

if (args.Contains("--run-session", StringComparer.Ordinal))
{
await PublishSavedWorkflow(runtime, workflowId, savedPath);
await RunWorkflowSession(runtime, workflowId);
}
else
Expand Down Expand Up @@ -173,6 +174,56 @@ await runtime.WorkflowGraphCloseEditSession(
$$"""{"session_id":{{JsonSerializer.Serialize(editSessionId)}}}""");
}

static async Task PublishSavedWorkflow(
FfiPantographRuntime runtime, string workflowId, string savedPath)
{
string fileJson = runtime.WorkflowGraphLoad(JsonSerializer.Serialize(new { path = savedPath }));
using JsonDocument file = JsonDocument.Parse(fileJson);
string editJson = await runtime.WorkflowGraphCreateEditSession(JsonSerializer.Serialize(new
{
workflow_id = workflowId,
graph = file.RootElement.GetProperty("graph"),
}));
string editSessionId = ReadString(editJson, "session_id");
string graphRevision = ReadString(editJson, "graph_revision");
try
{
string validationJson = await runtime.WorkflowGraphRefreshCurrentValidationSummary(
JsonSerializer.Serialize(new
{
graph_session_id = editSessionId,
graph_revision = graphRevision,
}));
using JsonDocument validation = JsonDocument.Parse(validationJson);
JsonElement summary = validation.RootElement.GetProperty("summary");
if (summary.GetProperty("state").GetString() != "current"
|| !summary.GetProperty("submit_gate").GetProperty("allowed").GetBoolean())
{
throw new InvalidOperationException($"Workflow validation is not executable: {validationJson}");
}
string validationSessionId = summary.GetProperty("validation_session_id").GetString()
?? throw new InvalidOperationException("Missing validation session identity");
string publicationJson = await runtime.PublishGraphSessionExecutableValidationSnapshot(
JsonSerializer.Serialize(new
{
workflow_id = workflowId,
workflow_semantic_version = "0.1.0",
graph_session_id = editSessionId,
validation_session_id = validationSessionId,
}));
if (ReadString(publicationJson, "validation_session_id") != validationSessionId
|| string.IsNullOrWhiteSpace(ReadString(publicationJson, "validation_snapshot_id")))
{
throw new InvalidOperationException($"Unexpected publication identity: {publicationJson}");
}
}
finally
{
await runtime.WorkflowGraphCloseEditSession(
JsonSerializer.Serialize(new { session_id = editSessionId }));
}
}

static async Task RunWorkflowSession(FfiPantographRuntime runtime, string workflowId)
{
string createResponse = await runtime.WorkflowCreateSession(
Expand Down
8 changes: 7 additions & 1 deletion bindings/csharp/Pantograph.DirectRuntimeQuickstart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,11 @@ your workflow and append `--run-session`.
- Loads the saved workflow through `WorkflowGraphLoad`.
- Opens a graph edit session, mutates node data, saves the edited graph, and
closes the graph edit session.
- When `--run-session` is passed: opens a workflow execution session, submits a
- When `--run-session` is passed: refreshes owner validation for the saved graph,
publishes its current executable snapshot, closes that edit session, opens a
workflow execution session, submits a
session run, prints outputs, and closes the workflow execution session.

The native runtime's attribution store is ephemeral. The example publishes again
each time it opens the runtime. Missing or stale validation is an error; the
example does not construct snapshot content or bypass admission.
54 changes: 54 additions & 0 deletions bindings/csharp/Pantograph.NativeSmoke/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ private static async Task RunTextSmoke(FfiPantographRuntime runtime, string proj
{
WriteTextWorkflow(projectRoot, TextWorkflowId);
ExerciseGraphAuthoringDiscovery(runtime);
await PublishSavedWorkflow(runtime, TextWorkflowId,
Path.Combine(projectRoot, ".pantograph", "workflows", $"{TextWorkflowId}.json"));

string createResponse = await runtime.WorkflowCreateSession(
$$"""{"workflow_id":"{{TextWorkflowId}}","keep_alive":true}""");
Expand Down Expand Up @@ -113,6 +115,56 @@ private static void ExerciseGraphAuthoringDiscovery(FfiPantographRuntime runtime
}
}

private static async Task PublishSavedWorkflow(
FfiPantographRuntime runtime, string workflowId, string savedPath)
{
string fileJson = runtime.WorkflowGraphLoad(JsonSerializer.Serialize(new { path = savedPath }));
using JsonDocument file = JsonDocument.Parse(fileJson);
string editJson = await runtime.WorkflowGraphCreateEditSession(JsonSerializer.Serialize(new
{
workflow_id = workflowId,
graph = file.RootElement.GetProperty("graph"),
}));
string editSessionId = ReadString(editJson, "session_id");
string graphRevision = ReadString(editJson, "graph_revision");
try
{
string validationJson = await runtime.WorkflowGraphRefreshCurrentValidationSummary(
JsonSerializer.Serialize(new
{
graph_session_id = editSessionId,
graph_revision = graphRevision,
}));
using JsonDocument validation = JsonDocument.Parse(validationJson);
JsonElement summary = validation.RootElement.GetProperty("summary");
if (summary.GetProperty("state").GetString() != "current"
|| !summary.GetProperty("submit_gate").GetProperty("allowed").GetBoolean())
{
throw new InvalidOperationException($"Workflow validation is not executable: {validationJson}");
}
string validationSessionId = summary.GetProperty("validation_session_id").GetString()
?? throw new InvalidOperationException("Missing validation session identity");
string publicationJson = await runtime.PublishGraphSessionExecutableValidationSnapshot(
JsonSerializer.Serialize(new
{
workflow_id = workflowId,
workflow_semantic_version = "0.1.0",
graph_session_id = editSessionId,
validation_session_id = validationSessionId,
}));
if (ReadString(publicationJson, "validation_session_id") != validationSessionId
|| string.IsNullOrWhiteSpace(ReadString(publicationJson, "validation_snapshot_id")))
{
throw new InvalidOperationException($"Unexpected publication identity: {publicationJson}");
}
}
finally
{
await runtime.WorkflowGraphCloseEditSession(
JsonSerializer.Serialize(new { session_id = editSessionId }));
}
}

private static async Task RunDiffusionSmoke(FfiPantographRuntime runtime, string projectRoot)
{
string modelId = RequireEnv("PANTOGRAPH_DIFFUSION_SMOKE_PUMAS_MODEL_ID");
Expand All @@ -123,6 +175,8 @@ private static async Task RunDiffusionSmoke(FfiPantographRuntime runtime, string
string outputPath = Environment.GetEnvironmentVariable("PANTOGRAPH_DIFFUSION_SMOKE_OUTPUT") ?? "";

WriteDiffusionWorkflow(projectRoot, DiffusionWorkflowId, modelId, pumasArtifactId);
await PublishSavedWorkflow(runtime, DiffusionWorkflowId,
Path.Combine(projectRoot, ".pantograph", "workflows", $"{DiffusionWorkflowId}.json"));

string createResponse = await runtime.WorkflowCreateSession(
$$"""{"workflow_id":"{{DiffusionWorkflowId}}","keep_alive":true}""");
Expand Down
30 changes: 30 additions & 0 deletions bindings/csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,36 @@ Runtime smoke coverage for generated Pantograph C# bindings.
| `Pantograph.DirectRuntimeQuickstart/` | Artifact-ready quickstart showing native save/list/load/edit-session/workflow-session usage from C#. |
| `PACKAGE-README.md` | README copied to the generated C# binding artifact. |

## Generator prerequisite

The maintained workflow uses UniFFI 0.28.3 and the official
[uniffi-bindgen-cs v0.9.0+v0.28.3 source](https://github.com/NordSecurity/uniffi-bindgen-cs/tree/2f4880f03ed08d960ad0ee14d11cf95444eee540).
The generator is not published as crates.io version 0.9.0. Install its exact
reviewed Git revision with the upstream lockfile:

```bash
cargo install uniffi-bindgen-cs \
--git https://github.com/NordSecurity/uniffi-bindgen-cs \
--rev 2f4880f03ed08d960ad0ee14d11cf95444eee540 --locked
```

This provisions a development/CI tool; it does not change the application's
UniFFI dependency or hand-edit generated bindings. The headless workflow owns
its pin and generated-artifact qualification. Keep the upstream MPL-2.0 notices
with any redistributed generator source/binary; this repository does not vendor
or package the generator itself. Existing generated-binding/artifact licensing
obligations remain unchanged.

## Shutdown result

Await `runtime.Shutdown()` and handle the generated `FfiException` if the
owned backend cannot stop. Its message contains the standard JSON error envelope
with `internal_error` and the original shutdown cause. A failed stop does not
mean the runtime released its residency; callers may retry after resolving the
cause. Existing C# await expressions remain valid, but regenerate bindings and
ship them with the matching native library when adopting this fallible API.
Rust callers now handle `Result<(), FfiError>` explicitly.

## Usage
Run the repository-level smoke script:

Expand Down
Loading
Loading