From 45ca74e12949b4efa3f4de3e8b12a4f8c9e70c9d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 May 2026 14:02:48 +0000 Subject: [PATCH 1/4] Initial plan From e20891a91e6fb6b65bc10ea6e1e01fa3cacdf61d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 May 2026 14:11:29 +0000 Subject: [PATCH 2/4] Add df.status_by_label() and clarify df.status() requires instance_id not label - Add `df.status_by_label(label TEXT)` Rust function in src/monitoring.rs that returns the status of the most recently started instance with the given label, returning NULL when no match exists (respects RLS) - Register the function in sql/pg_durable--0.1.1.sql - Update docs/api-reference.md: add note on df.status() warning about label vs instance_id confusion, and add full df.status_by_label() documentation entry - Update USER_GUIDE.md Quick Status Check section with df.status_by_label() example and a tip explaining the label vs instance_id distinction - Update .github/skills/pg-durable-sql/SKILL.md with status_by_label reference - Extend tests/e2e/sql/05_monitoring_and_explain.sql to exercise status_by_label for both a known label and an unknown label Co-authored-by: pinodeca <32303022+pinodeca@users.noreply.github.com> --- .github/skills/pg-durable-sql/SKILL.md | 6 ++++- USER_GUIDE.md | 10 +++++++- docs/api-reference.md | 27 +++++++++++++++++++++ sql/pg_durable--0.1.1.sql | 11 +++++++++ src/monitoring.rs | 19 +++++++++++++++ tests/e2e/sql/05_monitoring_and_explain.sql | 21 +++++++++++++++- 6 files changed, 91 insertions(+), 3 deletions(-) diff --git a/.github/skills/pg-durable-sql/SKILL.md b/.github/skills/pg-durable-sql/SKILL.md index c623139d..d1e82574 100644 --- a/.github/skills/pg-durable-sql/SKILL.md +++ b/.github/skills/pg-durable-sql/SKILL.md @@ -120,9 +120,13 @@ df.signal( Use a JSON object when workflow SQL expects structured fields; use plain text for simple opaque values. --- Query status +-- Query status by instance ID (returned by df.start()) df.status(instance_id TEXT) → TEXT -- 'Running', 'Completed', 'Failed', 'Cancelled' +-- Query status by label (most recently started instance with that label) +-- NOTE: df.status() requires an instance_id, NOT a label — use df.status_by_label() when you only have the label +df.status_by_label(label TEXT) → TEXT -- 'Running', 'Completed', 'Failed', 'Cancelled', or NULL + -- Get result df.result(instance_id TEXT) → TEXT -- JSON result from final node diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 45941e98..de3de915 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -1442,13 +1442,21 @@ SELECT * FROM df.metrics(); ### Quick Status Check ```sql --- Status only +-- Status by instance ID (returned by df.start()) SELECT df.status('a1b2c3d4'); +-- Status by label — returns the most recently started instance with that label +SELECT df.status_by_label('my-workflow'); + -- Result only SELECT df.result('a1b2c3d4'); ``` +> **Tip:** `df.status()` requires the `instance_id` returned by `df.start()`, not a label. +> Passing a label to `df.status()` returns `NULL` because no instance ID matches that string. +> Use `df.status_by_label()` when you only have the label, and save the `instance_id` from +> `df.start()` when you need to track a specific run. + ### Worker Liveness Check whether the background worker is alive and healthy: diff --git a/docs/api-reference.md b/docs/api-reference.md index 05fb16ab..2c013686 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -320,12 +320,39 @@ Gets instance status. |-----------|------|-----------|-------------| | `instance_id` | TEXT | ❌ Literal | Target instance ID | +> **Note:** `df.status()` requires an `instance_id` (the 8-character hex value returned by +> `df.start()`), **not** a label. Passing a label returns `NULL` because no instance ID +> matches that string. Use `df.status_by_label()` to look up status by label instead. + ```sql SELECT df.status('a1b2c3d4'); -- Returns: 'Running', 'Completed', etc. ``` --- +### df.status_by_label(label) + +Gets the status of the **most recently started** instance with the given label. +Returns `NULL` when no matching instance exists or is visible to the calling user. + +Labels are not unique — if multiple instances share the same label, only the +status of the most recently created one is returned. To target a specific run, +capture the `instance_id` from `df.start()` and use `df.status()` instead. + +| Parameter | Type | Auto-wrap | Description | +|-----------|------|-----------|-------------| +| `label` | TEXT | ❌ Literal | Label passed to `df.start()` | + +```sql +-- Start a labeled workflow +SELECT df.start('SELECT 1', 'my-workflow'); + +-- Check its status by label +SELECT df.status_by_label('my-workflow'); -- Returns: 'Running', 'Completed', etc. +``` + +--- + ### df.result(instance_id) Gets instance result (for completed instances). diff --git a/sql/pg_durable--0.1.1.sql b/sql/pg_durable--0.1.1.sql index 221c1cdb..71b0b2f9 100644 --- a/sql/pg_durable--0.1.1.sql +++ b/sql/pg_durable--0.1.1.sql @@ -3713,6 +3713,17 @@ LANGUAGE c /* Rust */ AS 'MODULE_PATHNAME', 'instance_info_wrapper'; /* */ +/* */ +-- src/monitoring.rs:111 +-- pg_durable::monitoring::status_by_label +CREATE FUNCTION df."status_by_label"( + "label" TEXT /* &str */ +) RETURNS TEXT /* core::option::Option */ +STRICT +LANGUAGE c /* Rust */ +AS 'MODULE_PATHNAME', 'status_by_label_wrapper'; +/* */ + /* */ -- src/dsl.rs:714 -- pg_durable::dsl::status diff --git a/src/monitoring.rs b/src/monitoring.rs index fabd7599..d8fccc88 100644 --- a/src/monitoring.rs +++ b/src/monitoring.rs @@ -108,6 +108,25 @@ pub fn list_instances( TableIterator::new(results) } +/// Gets the status of the most recently started instance with the given label. +/// +/// Labels are not unique — multiple instances may share the same label. This +/// function always returns the status of the **most recently created** matching +/// instance. If no instance with the given label exists (or is visible to the +/// calling user), `NULL` is returned. +/// +/// To check the status of a specific run, use `df.status(instance_id)` with +/// the `instance_id` returned by `df.start()`. +#[pg_extern(schema = "df")] +pub fn status_by_label(label: &str) -> Option { + Spi::get_one_with_args::( + "SELECT status FROM df.instances WHERE label = $1 ORDER BY created_at DESC LIMIT 1", + &[label.into()], + ) + .ok() + .flatten() +} + /// Get detailed info about a specific durable function instance. #[pg_extern(schema = "df")] pub fn instance_info( diff --git a/tests/e2e/sql/05_monitoring_and_explain.sql b/tests/e2e/sql/05_monitoring_and_explain.sql index 4996ae43..9de8b9d6 100644 --- a/tests/e2e/sql/05_monitoring_and_explain.sql +++ b/tests/e2e/sql/05_monitoring_and_explain.sql @@ -1,5 +1,5 @@ -- Merged from: 09_monitoring, 10_explain, 31_explain_plain_sql --- Tests: list_instances, instance_info, status, result, df.explain() on live and dry-run, +-- Tests: list_instances, instance_info, status, status_by_label, result, df.explain() on live and dry-run, -- df.explain() on plain SQL auto-wrap SET SESSION AUTHORIZATION df_e2e_user; @@ -16,6 +16,8 @@ DECLARE found BOOLEAN; info_status TEXT; result TEXT; + label_status TEXT; + missing_status TEXT; BEGIN SELECT instance_id INTO inst_id FROM _test_state; RAISE NOTICE 'Testing instance: %', inst_id; @@ -48,6 +50,23 @@ BEGIN IF result NOT LIKE '%123%' THEN RAISE EXCEPTION 'TEST FAILED: result should contain 123, got %', result; END IF; + + -- Test status_by_label returns the same status as df.status() + SELECT df.status_by_label('test-monitoring-label') INTO label_status; + IF label_status IS NULL THEN + RAISE EXCEPTION 'TEST FAILED: status_by_label returned NULL for known label'; + END IF; + IF lower(label_status) != lower(status) THEN + RAISE EXCEPTION 'TEST FAILED: status_by_label (%) != status (%) for same instance', + label_status, status; + END IF; + + -- Test status_by_label returns NULL for an unknown label + SELECT df.status_by_label('__nonexistent_label_xyz__') INTO missing_status; + IF missing_status IS NOT NULL THEN + RAISE EXCEPTION 'TEST FAILED: status_by_label should return NULL for unknown label, got %', + missing_status; + END IF; RAISE NOTICE 'TEST PASSED: monitoring'; END $$; From 55afc907bff4023a932e0ad549eccaf64624f3f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 May 2026 14:16:00 +0000 Subject: [PATCH 3/4] Add df.status_by_label to upgrade script and upgrade-testing.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add CREATE OR REPLACE FUNCTION df.status_by_label to the 0.2.1→0.2.2 upgrade script so users upgrading from 0.2.1 also receive the new function - Update docs/upgrade-testing.md to include df.status_by_label() in the monitoring functions table for upgrade scenario tests Co-authored-by: pinodeca <32303022+pinodeca@users.noreply.github.com> --- docs/upgrade-testing.md | 2 +- sql/pg_durable--0.2.1--0.2.2.sql | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/upgrade-testing.md b/docs/upgrade-testing.md index 567b831d..9b02dd53 100644 --- a/docs/upgrade-testing.md +++ b/docs/upgrade-testing.md @@ -72,7 +72,7 @@ We test against all previous versions in the same provider compatibility line. T | Variable capture | `df.start()` with vars set | | DSL construction | `df.sql()`, `df.seq()`, `df.if()`, `df.loop()`, `df.sleep()`, `df.http()` | | Execution | Starting and completing orchestrations | -| Monitoring | `df.status()`, `df.result()`, `df.list_instances()`, `df.instance_info()` | +| Monitoring | `df.status()`, `df.status_by_label()`, `df.result()`, `df.list_instances()`, `df.instance_info()` | | In-flight work | Orchestrations started before `.so` swap complete after swap | **What it catches:** diff --git a/sql/pg_durable--0.2.1--0.2.2.sql b/sql/pg_durable--0.2.1--0.2.2.sql index 0820b630..814ee877 100644 --- a/sql/pg_durable--0.2.1--0.2.2.sql +++ b/sql/pg_durable--0.2.1--0.2.2.sql @@ -9,6 +9,9 @@ -- The fix runs the name through quote_ident() first so regrole_in() sees a -- properly quoted identifier and resolves the role without case folding. -- See issue #161 / PR #162. +-- +-- Add: df.status_by_label(label TEXT) — ergonomic status lookup by label. +-- See issue #165. -- ---------------------------------------------------------------------------- -- df.instances policy @@ -39,3 +42,14 @@ CREATE POLICY vars_user_isolation ON df.vars ALTER TABLE df.vars ALTER COLUMN owner SET DEFAULT quote_ident(current_user)::regrole; + +-- ---------------------------------------------------------------------------- +-- df.status_by_label(label TEXT) — new in 0.2.2 +-- Returns the status of the most recently started instance with the given label. +-- Returns NULL when no matching instance is visible to the calling user (RLS). +-- ---------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION df.status_by_label("label" TEXT) +RETURNS TEXT +STRICT +LANGUAGE c +AS 'pg_durable', 'status_by_label_wrapper'; From 21648522e3fe27045b1aea06873321bd447fdd07 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 May 2026 14:17:36 +0000 Subject: [PATCH 4/4] Address review feedback: fix docs wording, clarify test comment, add error handling note - docs/api-reference.md: remove 'eight-character hex' from df.status() note; use 'unique identifier returned by df.start()' instead - tests/e2e/sql/05_monitoring_and_explain.sql: add comment clarifying status_by_label test uses the same label as the df.start() call above it - src/monitoring.rs: document the NULL-on-error behaviour of status_by_label and note its intentional consistency with df.status() Co-authored-by: pinodeca <32303022+pinodeca@users.noreply.github.com> --- docs/api-reference.md | 2 +- src/monitoring.rs | 4 ++++ tests/e2e/sql/05_monitoring_and_explain.sql | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index 2c013686..9f2698e4 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -320,7 +320,7 @@ Gets instance status. |-----------|------|-----------|-------------| | `instance_id` | TEXT | ❌ Literal | Target instance ID | -> **Note:** `df.status()` requires an `instance_id` (the 8-character hex value returned by +> **Note:** `df.status()` requires an `instance_id` (the unique identifier returned by > `df.start()`), **not** a label. Passing a label returns `NULL` because no instance ID > matches that string. Use `df.status_by_label()` to look up status by label instead. diff --git a/src/monitoring.rs b/src/monitoring.rs index d8fccc88..12845f89 100644 --- a/src/monitoring.rs +++ b/src/monitoring.rs @@ -117,6 +117,10 @@ pub fn list_instances( /// /// To check the status of a specific run, use `df.status(instance_id)` with /// the `instance_id` returned by `df.start()`. +/// +/// Note: SPI errors are mapped to `NULL` (same behaviour as `df.status()`). +/// A `NULL` return means either "no matching instance" or an internal error; +/// use `df.list_instances()` when you need to distinguish the two. #[pg_extern(schema = "df")] pub fn status_by_label(label: &str) -> Option { Spi::get_one_with_args::( diff --git a/tests/e2e/sql/05_monitoring_and_explain.sql b/tests/e2e/sql/05_monitoring_and_explain.sql index 9de8b9d6..40144f04 100644 --- a/tests/e2e/sql/05_monitoring_and_explain.sql +++ b/tests/e2e/sql/05_monitoring_and_explain.sql @@ -52,6 +52,7 @@ BEGIN END IF; -- Test status_by_label returns the same status as df.status() + -- (uses the same label passed to df.start() above: 'test-monitoring-label') SELECT df.status_by_label('test-monitoring-label') INTO label_status; IF label_status IS NULL THEN RAISE EXCEPTION 'TEST FAILED: status_by_label returned NULL for known label';