Commit 33033a3
feat(sources): Slack source pack (milestone 5.2) + built-in packs as embedded YAML (#172)
* feat(sources): GitHub source pack for Open Connector (milestone 5.1)
The first real provider pack: repositories, issues, issue comments,
pull requests, reviews, commits, workflow runs, and releases as stable
SQL tables (packs/github.rs, page-number pagination at GitHub's
100-row maximum), queryable through YAML bindings, open_connector_query,
and federated joins.
Engine additions the pack required, each sanctioned by the design spec:
- Per-mapping filter Fidelity (filters.rs): Exact mappings stay fully
provider-side; Inexact mappings narrow the fetch and DataFusion
reapplies the predicate. issues.updated_at >= maps to GitHub's
`since` as Inexact (documented "updated at or after" — a guaranteed
superset); commits' strictly-after `since` is deliberately NOT mapped,
because a boundary row the provider drops is unrecoverable — the mock
pack's `>=` rule applied for real. Timestamp filter literals render
as RFC 3339 UTC strings at every DataFusion granularity.
- Utf8ListFromObjectKey (json_to_arrow.rs): the design's
`$.labels[*].name` / `$.assignees[*].login` flattening — an array of
objects becomes List<Utf8> of one declared key.
- A JSON-null *parent* on a nested path is absence, not a structural
failure: nullable leaves under `commit.author: null` /
`issue.user: null` become SQL NULL (required ones still fail with a
targeted error). Extends the previous PR's JSON-null-to-SQL-NULL rule
to nested paths.
- SourcePackTable::fixed_inputs: issues/pull_requests pin `state=all`
so SELECT * reads the complete collection (GitHub defaults to open
items only); a pushed state predicate overrides the pin, keeping
SELECT * and WHERE state='closed' consistent.
Relational caveats encoded rather than papered over: GitHub's issues
endpoint returns pull requests, so the stable schema exposes the
pull_request marker as nullable opaque JSON (IS NULL selects pure
issues). Nullability is conservative — identity fields only.
Fingerprint pins stay None like the mock pack until validated against
a live gateway's discovered contracts; the redacted per-table fixtures
under packs/fixtures/github/ are the build-time conversion contract.
Tests: 180 open_connector total (was 158 on main). Fixture contract
suites for all 8 tables (null-bearing, null-parent, empty-list,
nested, extra-field rows; empty pages keep the stable schema),
bind-time validation of every contract, and end to end through the
mock gateway: a 150-row two-page scan carrying state=all and the
resource inputs on every request, Exact state override, Inexact since
narrowing plus local re-filter (gateway ignores since entirely — the
harshest legal Inexact provider — and the boundary row is kept),
pull_request IS NULL / IS NOT NULL, LIMIT stopping after one page, and
open_connector_query parity with the YAML-bound table.
Docs: docs/open-connector-github.md (per-table filter/limit behavior,
authorization/visibility incl. the issues-returns-PRs caveat, rate limits,
freshness, compatibility), open-connector.md status, README row,
milestone 5.1 ticked in the tasks spec.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): runnable GitHub-pack demo in the db-source demo style
docs/open-connector/ mirrors the postgres/dynamodb demo shape: a README
quick start whose every command was executed against the real server
before being written down, a committed ctx, sample CSV, and four
pipelines (stable table with state pushdown + pull_request IS NULL,
open_connector_query, open_connector_scan, federated CSV join).
The remote service is played by a bundled ~200-line stdlib-Python stub
gateway — the same role DynamoDB Local plays in the DynamoDB demo: it
speaks the /v1 contract (health, discovery with read_only metadata and
an output schema rich enough for raw-scan type derivation, paginated
execution with GitHub-style state/since filtering), so the entire
Skardi side runs unmodified and offline, credential-free, in CI-able
form. A closing section documents the real-gateway path (deploy Open
Connector, GitHub connection there, runtime token, one connection_string
change) and honestly flags it as pending live validation — the same
caveat as the pack's fingerprint pins.
Linked from the general guide, the GitHub guide, and the README
supported-sources row; tasks spec 5.1 note extended.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): honor the nullable-item contract in list conversion
Both list variants failed conversion on any JSON null in item position
— a null element in a Utf8List, or a pluck key holding null in
Utf8ListFromObjectKey — even though the Arrow item field is declared
nullable. An explicit provider null is data and now converts to an
Arrow null item.
The line drawn deliberately stops there: a *missing* pluck key still
fails. Unlike columns, items carry no pack-declared nullability that
authorizes reading absence as null, so a dropped upstream key (shape
drift) fails loudly instead of silently yielding all-null lists. The
shape-mismatch errors now name the specific kind found ("array element
whose 'name' is number", "string array element", "array element
without key 'name'") instead of one blanket message.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): translate Utf8View string literals in filter pushdown
DataFusion 52 can carry string literals as Utf8View scalars after
coercion; scalar_to_json missed the variant, silently demoting Exact
predicates like state = 'open' from a provider-side pushdown to a
local re-filter over the full (state=all-pinned) fetch. Results stayed
correct — Unsupported classification means DataFusion re-evaluates —
but the narrowing was lost. Note the review's suggested LargeUtf8View
arm does not exist: Arrow view types have no Large variants. Test pins
LargeUtf8 and Utf8View both translating Exact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): fold cast-wrapped literals in filter pushdown
translate_one only matched bare column-vs-literal comparisons, so a
literal that type coercion wrapped in CAST/TRY_CAST (the planner's
shape for `updated_at >= '2026-01-01'` against a timestamp column)
silently stayed local instead of narrowing the fetch.
Evaluate the cast rather than strip it, per the review's intent but not
its letter: resolve_literal folds literal-only cast chains with
ScalarValue::cast_to — the same Arrow kernel the engine would run — so
Exact semantics stay exact (CAST('10' AS DOUBLE) pushes the number 10,
never the string "10", which a strip would have sent). A failing cast
or non-literal operand classifies Unsupported and is evaluated locally,
and a cast around the COLUMN side (`CAST(updated_at AS DATE) >= …`,
which changes predicate semantics) is never matched.
Tests: coercion-shaped timestamp cast pushes `since` as Inexact,
numeric TryCast pushes a JSON number as Exact, unparseable casts and
column-side casts both stay Unsupported.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): reject ambiguous short table names in pack lookup
Table lookup matched a binding's short name against the ID's last
segment and returned the first hit. The comparison is whole-segment
equality, so the review's example (issues vs closed_issues) never
collided — but the underlying concern is real for multi-segment IDs
(github.issue.comments vs github.pr.comments both end in "comments"),
where first-match would silently bind the wrong relational contract.
Lookup now tries an exact full-ID match first, then the short-name
convention, and a short name matching several tables is a targeted
SourcePackTableAmbiguous error listing the candidates instead of a
silent first-wins. A new invariant test pins every built-in pack to
namespaced `<pack>.<table>` IDs with unique last segments, so the
ambiguity path is reachable only by future multi-segment or
user-authored packs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): reject '$.'-prefixed column paths at converter build
RowConverter::from_columns prepends '$.' to every column path, so a
path already carrying the marker became '$.$.foo' — which, contrary to
first appearance, PARSES: RowPath strips one '$.' and keeps a literal
'"$"' first segment, silently reading the wrong key (all-NULL nullable
columns, confusing "missing key" failures otherwise). No current caller
can produce this (pack paths are reviewed, raw-scan columns reject
dotted names), but ColumnSpec is public API.
Reject rather than strip: column paths are row-relative by contract,
and the error names the corrected spelling ("write 'user.login', not
'$.user.login'"). Test pins the rejection and the message.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): keep YAML resource-value types instead of stringifying
OpenConnectorBinding.resource was BTreeMap<String, String>, so a YAML
binding's `issue_number: 42` reached the gateway as the string "42"
while the UDTFs' resource JSON sent the number 42 — inconsistent action
inputs for the same logical scan, numeric resources (issue_comments,
reviews) forced through strings, and — the unflagged consequence — a
binding and an identical UDTF invocation computed different scan-cache
keys, so the shared cache could never match across the two interfaces.
resource is now BTreeMap<String, serde_json::Value>: YAML scalars keep
their types and flow into the action input (and the cache key) exactly
as the UDTF path sends them. Null values are rejected at validation
(NullResourceValue) — a null would satisfy the required-key presence
check while sending `null` to the gateway. The GitHub guide's
"forwarded as strings" caveat is replaced by the new guarantee.
Tests: YAML type preservation and null rejection at config level, and
an end-to-end pin that a bound issue_comments scan sends
"issue_number":42 (never "42") in every gateway request body.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* style(sources): use the imported Value alias for the resource map
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(sources): typed fixed_inputs via a const-friendly FixedValue
fixed_inputs could only carry &'static str values, so a future numeric
or boolean pinned default would have been forced through a string —
the same class of stringification the resource map just shed. The
review's literal suggestion (&'static [(&str, serde_json::Value)])
cannot compile: Value's String and Number variants are not
constructible in static initializers. Introduce FixedValue
(Str/Int/Float/Bool, Copy, const-friendly) converting to the JSON
scalar at input-assembly time; the github pack's state=all pins move to
FixedValue::Str. Behavior unchanged for existing packs; a test pins the
scalar conversions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): reconcile the cache rules with their key-scoped reality
Two review findings, both intentional behavior whose written rules had
drifted:
1. The design doc still stated the original milestone-2-era rule "a
LIMIT-stopped scan is never cached", while milestone 3 deliberately
refined it: LIMIT is part of the cache key, so a LIMIT-satisfied
scan is complete for its key and is stored (cache.rs docs and the
tasks spec already said so). Update the design doc to the key-scoped
rule, note the refinement explicitly, and name LIMIT's key
membership as the load-bearing invariant. A new regression test pins
it harder than prose can: a full scan issued after a cached LIMIT
scan must fetch live and return all rows — if limit ever falls out
of the key, that test replays the truncated entry and fails.
2. The cache key omits fixed_inputs, which the design's key list
includes. Safe today because pack-pinned inputs are functionally
determined by (action_id, source_pack_version), both already keyed —
now stated at the ScanKeyParts construction so the transitive
argument survives a future in which fixed inputs become
binding-configurable or vary within a pack version.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): run pull_requests' state pin/override end to end
pull_requests is the only table besides issues declaring the state=all
fixed input plus an Exact state filter, but its pin/override path never
ran through the real scan engine — the end-to-end coverage was
issues-only. Add the pull_requests variant: a plain scan carries
"state":"all" on every request and exposes closed PRs (the pin's whole
point — GitHub's endpoint defaults to open only), and a state = 'open'
predicate replaces the pin in the action input, verified in the
recorded request bodies.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): cover pull_number in the numeric-resource assertion
Numeric-resource forwarding was pinned for issue_comments
(issue_number) only; reviews consumes the same path through
pull_number. The test now registers both bindings and asserts, per
action, that the value reaches the gateway as a JSON number
("issue_number":42 / "pull_number":7) and never stringified — covering
both declared consumers of numeric resources.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): guard the deliberately-unmapped commits/workflow filters
The pack argues at length (module docs, guide) that commits must NOT
map committed_at to the endpoint's `since` — GitHub documents it as
strictly-after, so a pushed `>=` would drop the boundary commit
unrecoverably — and that workflow_runs must not map `status`, whose
provider parameter also matches conclusion values. Neither decision had
a regression test, so a future accidental mapping would land silently
with Exact classification and wrong rows.
Two guards now run the real scan engine against a stub gateway and
assert the request bodies carry no `since` / no `status` while
DataFusion filters locally (the boundary commit stays; only completed
runs return) — the same shape as the mock pack's
gteq_is_not_pushed_to_strict_gt_input precedent. A shared setup_table
helper registers any single non-issues table for these and future
per-table tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): scan the total_count-bearing envelope end to end
workflow_runs is the pack's one structurally different response shape —
the row array sits beside a sibling total_count, GitHub's actual
envelope — but every end-to-end scan used the unwrapped issues shape
(the fixture test covered extraction only, and the status-filter test
was single-page without the sibling). A 150-run scan now paginates
through the wrapped envelope: two pages at per_page=100, the short page
terminates, and the sibling key stays inert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): pin the GitHub binding's required-resource contract
A binding without `repo` must fail registration with
MissingResourceInput naming the binding and key — asserted here for the
first time on the registration path at all: the only prior
MissingResourceInput assertion was the UDTF planning path (mock pack),
so the pack contract AND the foundation's registration-time check were
both riding untested. The test also pins ordering: enforcement fires
after the health check but before any action-discovery request leaves
the process (the recorded gateway traffic is health-only).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): drive empty-page termination through a pack scan
The wrapped-envelope test terminates on a short page (50 < 100); the
exact-boundary path — a full 100-row page that cannot signal
completion, forcing one more request that returns empty — ran only in
the pagination unit tests, never through a GitHub table. A 100-run
scan now pins it end to end: two requests, all 100 rows emitted, the
empty page 2 ends the scan. The workflow_runs stub/registration moved
into a shared setup_workflow_runs(total, env) helper serving both
termination modes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): classify the state pushdowns Inexact, not Exact
The state = X translation is faithful only inside GitHub's enum domain
(open/closed/all). Exact told DataFusion not to re-apply the predicate,
so correctness for an out-of-domain literal — state = 'merged' on PRs
is a natural thing to try — hinged entirely on the provider rejecting
the value: a provider that silently ignored it and returned its default
listing would hand back open rows as the answer to a query whose truth
is empty. GitHub 422s today, but the Exact claim was leaning on
provider validation the contract shouldn't assume.
Both state mappings (issues, pull_requests) are now Inexact: the push
still narrows the fetch identically on well-behaved providers, and the
local re-check is essentially free — state is a column of every output
row. A regression test drives the motivating scenario through a stub
that ignores the state input: state = 'merged' returns zero rows while
the push still appears in the request body. Docs and the module's
filter-fidelity bullet updated to state the enum-domain rule.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): record the workflow_runs pushdown follow-up in place
Per review: `event` and `head_branch` (GitHub's `branch` query param)
are faithful narrowing candidates left unmapped — fetch-reduction only,
since local filtering already gives correct results. Note them at the
filters declaration so the follow-up isn't lost, with one correction to
the review's framing: per the module's string-enum push rule they land
as Inexact, not Exact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): state the unpinned-fingerprint failure mode and follow-up
Per review: the "no fingerprint pins yet" comment now spells out the
operational consequence — incompatible upstream drift surfaces only at
scan time as a terminal ConversionFailed, for every query on the table
(conversion builds all declared columns before projection), with no
binding-side schema patch by design, so the fix is a pack version
bump — and points at the tracked follow-up: validate against a live
gateway (endpoint-contract check included) and pin the fingerprints so
drift fails fast at registration.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(sources): Slack source pack — conversations, users, files
Milestone 5.2 of the Open Connector integration: the second real
provider pack, validating cursor pagination (Slack's cursor /
response_metadata.next_cursor contract) the way the GitHub pack
validated page-number pagination.
Tables (bot-token visibility, no required resources):
- conversations: cursor-paginated channels with the
types=public_channel,private_channel pin, so the table reads as the
complete collection the bot can see (Slack defaults to public-only);
IMs/MPIMs deliberately excluded from a channels table.
- users: cursor-paginated members incl. bots and deleted users; email
is scope-gated (users:read.email) and NULL without it.
- files: Slack's classic page/count pagination (that endpoint never
adopted cursors); user_id -> user pushed as Inexact per the
string-push rule; created >= -> ts_from deliberately NOT mapped
(Slack takes epoch seconds; the filter engine renders timestamp
literals as RFC 3339 only — a candidate once per-mapping value
rendering exists).
No message/thread tables, per the design's Slack caveat: upstream
lacks complete message-cursor handling, and an incomplete message
table would violate the admission gate's complete-pagination rule.
Documented in the module docs and the guide.
New engine support: FieldType::TimestampSecondsUtc — Slack's
created/updated are epoch seconds, and the millis reader would have
silently produced January-1970 dates. Strictly integers; fractional
and string values fail with their kind.
Verification: 32 new tests (212 open_connector total). Fixture
contract tests per table (nested topic.value/profile.*, scope-gated
email, deleted users, empty channel lists, Slack's empty-string
convention, epoch-seconds columns, empty pages); end to end via the
mock gateway: multi-page cursor scan (no cursor on page 1, the stub's
token afterwards, limit hint + types pin on every request), BOTH
termination spellings (empty-string cursor and absent
response_metadata), pagination-loop detection bounded at the first
repeated cursor, LIMIT early stop, empty workspace, user_id pushed and
re-applied against an ignoring provider, multi-table binding with zero
required resources, UDTF parity for slack.users. Docs:
docs/open-connector-slack.md plus guide/README/tasks-spec updates.
Stacked on feature/open-connector-github-pack (#168): depends on its
Fidelity::Inexact, fixed_inputs/FixedValue, fixtures convention, and
list/null conversion rules.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): trust Slack's paging.pages instead of the short-page heuristic
The files table used PageNumber's short/empty-page termination, but
Slack can legally return short non-final pages (permission filtering,
deletions), which the heuristic reads as end-of-collection — silently
truncating the scan, against the no-partial-result invariant. Slack's
envelope carries an authoritative total, so use it.
PageNumber gains an optional total_pages_path (Slack: $.paging.pages),
parsed and validated at bind time like the cursor path. When declared,
only page >= pages ends the scan — short and even empty non-final
pages keep going (bounded by max_pages) — and a missing or non-numeric
total fails loudly (RowPathNotFound / new PaginationTotalInvalid)
rather than falling back to the truncating heuristic. Without the
path, the heuristic remains: it is all providers like GitHub give
(mock/github pass None; their behavior is unchanged).
Tests: unit coverage for short/empty middle pages continuing, pages=0
stopping, missing and non-numeric totals failing, malformed paths
rejected at bind; an end-to-end scan drives the motivating scenario —
three pages of sizes 2/1/2 with paging.pages=3 return all five rows in
exactly three requests. Docs updated (module bullet, guide, tasks
spec).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(sources): per-mapping ValueFormat; push files.created as ts_from
scalar_to_json rendered every timestamp literal as RFC 3339, which is
why files.created >= stayed unmapped — Slack's ts_from takes epoch
seconds. Per review, that global assumption was also a latent hazard:
a future pack mapping a timestamp to an epoch-seconds input would have
silently sent the wrong spelling with no compiler or test to catch it.
FilterMapping gains value_format (Rfc3339 / EpochSeconds), so the
provider's spelling is declared where the mapping is declared.
EpochSeconds floors sub-second precision, which widens a LOWER bound
(a superset, trimmed back by Inexact re-filtering) but would narrow an
upper bound — the enum docs pin epoch-seconds mappings to
ts_from-style lower-bound inputs only. Existing mappings take Rfc3339;
behavior unchanged.
The workaround this replaces is gone rather than recorded as tech
debt: files.created >= now maps to ts_from (Inexact + EpochSeconds;
Slack documents ts_from as inclusive, so >= holds a superset even
before flooring). Unit tests pin whole-second rendering across all
four timestamp granularities plus the flooring case; an end-to-end
scan asserts ts_from goes out as epoch seconds — never RFC 3339 —
against a stub that ignores it, with the boundary row kept by the
local re-filter. Docs updated (module bullet, guide, tasks spec).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): diagnose out-of-range epoch seconds precisely
TimestampSecondsUtc collapsed a checked_mul overflow into the same None
as "not an integer", so the error read found: "number" against
expected: "epoch-seconds timestamp" — contradictory, since the value is
a number; it is simply beyond the millisecond-representable range.
collect_cells now delegates to collect_cells_described, whose converter
returns the found-description itself; the seconds arm distinguishes the
two failure modes ("an epoch second out of range for millisecond
timestamps" vs the JSON kind). The other arms keep the Option-based
API unchanged. Test extended with an i64::MAX case pinning the
distinct message.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): skip pagination advance once a LIMIT completes the scan
After a LIMIT-satisfied page set done, the scan still called
pagination.advance() on that final envelope. Beyond the wasted work the
review flagged (a next_token that is never used, a re-parsed cursor),
advance() validates continuation state — so a repeated cursor (loop
detection) or a missing/malformed total-pages entry ON THE FINAL PAGE
failed a scan whose result was already complete for its cache key.
Advance now runs only while the scan is still going. A regression test
drives the sharpened case end to end: a stuck-cursor gateway whose
repetition would trip loop detection on page 2, with LIMIT 4 satisfied
exactly there — the scan returns its 4 rows in 2 requests instead of
failing on continuation state it will never use.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): correct the 5.2 verification test counts
The blurb claimed 212 open_connector tests with 32 new; the real
figures are 219 total with 20 new relative to the 5.1 branch point
(the review flagged 214/13, a snapshot of the initial commit before
four review-round fixes each added tests, offset by two phantom grep
matches in comments). State the counting method alongside the numbers
so the next update is mechanical: cargo test -p skardi --lib
sources::providers::open_connector, diffed against the branch point.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): assert request inputs structurally, not by substring
Per review, the negative substring check !bodies[0].contains("cursor")
was brittle — any field whose value happens to contain the substring
would fail it. Parse the request body and assert on
input.get("cursor") instead; the cursor test's positive and pin
assertions move to the same structural form.
The two GitHub never-pushed guards (!contains("since") /
!contains("status")) were the same brittle class with inverted stakes:
a stray substring in an unrelated field would false-FAIL them, and the
structural check is what their intent ("this key never reaches the
provider input") actually says. Converted alongside.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): assert which rows survive the Inexact re-filter
Per review, the files user-filter test counted one surviving row
without checking it was F0001 — a re-filter keeping the wrong row would
have passed. Assert row identity instead of cardinality, via a small
ids_of helper; the ts_from boundary test had the same weakness (its
message claimed "boundary row F0002 stays" while only counting two
rows) and now pins [F0002, F0003] explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): bind all three tables where the comment claims it
Per review, the multi-table binding test said "exposes all three
tables" while binding only users and files. Bind conversations too —
registration only needs discovery, which the stub's generic arm
already serves — and assert the binding schema actually lists all
three tables, so the comment's claim is now checked rather than
merely stated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(sources): surface Slack's in-band ok:false errors as themselves
Slack reports application errors as HTTP 200 with ok:false + error, so
through the gateway's output envelope a not_authed/missing_scope page
used to die on row extraction as "row path '$.channels' failed: the key
is missing" — the real cause buried under a shape error. Per review,
a test alone could not make that meaningful; the pack needed a way to
say where in-band errors live.
SourcePackTable gains error_path (Slack: $.error), carried on
ScanTarget, parsed and validated at bind time like the other
pack-authored paths. Before each page's row extraction the scan checks
it: a present, non-null value fails the scan as ProviderReportedError
naming the action, page, and the provider's own code (a short
identifier, bounded at 128 chars; non-string values report their JSON
kind). Success envelopes carry no error key, so the happy path costs
one failed map lookup. GitHub/mock stay None — their providers error
at the HTTP level, which the client already surfaces.
An end-to-end test drives ok:false + missing_scope through the pack:
the error names missing_scope and slack.list_conversations and never
mentions a row path. Guide updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): send Open Connector's camelCase perPage, not GitHub's per_page
Verified against a live gateway: OC action-input schemas are camelCase
and strict (additionalProperties rejected), so
POST /v1/actions/github.list_repository_issues with {"per_page":5,"page":1}
returns HTTP 400 ("Action input does not match the action schema"),
while {"perPage":5,"page":1} returns HTTP 200 — matching the perPage/page
inputs in the action's published contract. GITHUB_PAGINATION now declares
perPage; page was already correct.
The mock-gateway stubs paged on the snake_case key, which is exactly why
CI stayed green while a live scan would 400 on page 1 — they now read
perPage, and the page-1 body assertion pins the camelCase key on the
wire. The demo stub gateway and module docs follow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): speak Open Connector's real HTTP protocol, verified live
Reconciled the client and the GitHub pack against a live Open Connector
gateway (v1.3.1, run from source) and its provider code. The mock-first
implementation had diverged from the real API in ways fixtures could
never catch — every item below is pinned by a live probe:
Client protocol:
- Execute is POST /v1/actions/{id} — the /execute suffix 404s on the
real gateway. Same path as discovery, different method.
- Every /v1 response is a uniform envelope {success, message, data,
meta, errorCode?}. Success output lives in data (the old code looked
for an output field and would have rejected every real success);
failures surface errorCode + message + meta.executionId, bounded, so
a 400 invalid_input or 403 authorization_failed reads as itself.
- Discovery metadata is camelCase and nested: data.inputSchema,
data.outputSchema, data.execution.locallyExecutable. The old
snake_case flat parse yielded all-None, which default-deny would have
turned into 'nothing is executable' against a real gateway.
- The connection-alias header is x-oo-connector-alias (was an invented
x-openconnector-connection-alias the gateway ignores, silently
routing to the default connection).
- connection_aliases removed from the model: the real discovery
response has no such field and nothing consumed it.
- read_only is parsed from execution.readOnly as forward-compat; the
real gateway publishes no read/write classification yet (verified in
source), so raw scans stay refused by the documented default-deny
gate and pack tables are unaffected. Docs now say this outright.
GitHub pack contract:
- repositories now binds github.list_my_repositories; the previously
declared github.list_repositories does not exist on the gateway
(registration would fail at discovery).
- issue_comments/reviews resource keys are issueNumber/pullNumber —
required, camelCase, additionalProperties:false in the live schemas.
- issues is pure issues: the OC action filters out the pull requests
GitHub's raw endpoint mixes in, so the pull_request marker column
(permanently NULL against the real gateway) is gone, with a
negative-space guard pinning the decision. Demo pipelines dropped
their pull_request IS NULL clauses.
Tests mock the real wire shape via new testutil envelope builders
(envelope_ok / envelope_err / discovery_ok); the demo stub gateway now
speaks the same envelope. 200 open_connector tests, 720 lib tests.
Environmental note: provider egress could not be exercised end-to-end
here (no GitHub credential; the machine's fake-IP DNS lands in
198.18/15, which OC's SSRF guard always blocks) — row-level validation
against live GitHub data remains open alongside fingerprint pinning.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: drop accidentally committed __pycache__ artifact
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(sources): scope binding resources per table to survive strict schemas
Second live-gateway finding: a binding's resource map was forwarded
wholesale to every bound table's action, but Open Connector action
schemas reject undeclared inputs (additionalProperties: false). Binding
repositories alongside issues under one owner/repo resource map made
every repositories scan 400 — github.list_my_repositories declares no
resource inputs at all. Verified live: before this change the scan
returned invalid_input; after it, the same query passes schema
validation (fails only at the expected credential wall).
- SourcePackTable grows optional_resources (e.g. a future Slack
channelId scoping list_files) next to required_resources, plus a
declares_resource helper.
- OpenConnectorTableProvider::new filters the binding resource to the
keys the table declares — both the YAML path and the UDTF path
construct the provider, so one enforcement point covers both.
- Registration rejects a resource key that NO bound table declares
(UnknownResourceKey): requests never carry it, so it is dead
configuration — almost certainly a typo — and silently dropping it
would hide the mistake.
- open_connector_query rejects undeclared keys at planning outright:
with a single table there is nothing else to consume them.
- Tests: a shared repositories+issues binding against a stub that
mirrors the live gateway's strictness (400 on undeclared keys),
structural per-action input assertions, registration typo rejection,
and the UDTF planning rejection. 202 open_connector tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): rebuild the Slack pack on Open Connector's real contract
Reconciled against a live gateway (v1.3.1) and the OC provider source:
unlike GitHub's raw-passthrough executors, OC's Slack executors
NORMALIZE — every declaration in this pack that came from Slack's raw
Web API was wrong on the wire.
Contract fixes (each one live-verified: all three tables' generated
inputs now pass the gateway's strict action schemas, reaching the
credential wall instead of 400 invalid_input):
- Row paths: conversations/users live under $.conversations/$.users
(not Slack's channels/members); rows are the normalized camelCase
shapes (channelId, isArchived, realName, …). Conversations and users
columns rebuilt on that contract — the normalized rows carry no
created/updated timestamps, no email/tz/team_id (users gain is_owner,
locale, and conversations a type classification). File rows are raw
Slack objects plus aliases, so the existing raw columns survive.
- Cursor: top-level $.nextCursor, null at end-of-collection (was
$.response_metadata.next_cursor). Both termination spellings pinned.
- types on conversations is an ARRAY of enum strings in the strict
schema; the comma-joined string pin would 400 every request. New
const-friendly FixedValue::StrList carries it.
- includeLocale pinned on users so the declared locale column is
actually populated.
- files.user_id pushes to the userId input (was Slack's raw user).
- The created >= → ts_from push is REMOVED: the OC list_files contract
declares no time input and its strict schema rejects one. A
negative-space guard asserts no time key ever reaches the wire; the
engine's ValueFormat stays for future packs.
- files gains an optional channelId resource (per-table resource
scoping from the github branch).
- error_path dropped from all three tables: the OC executor consumes
Slack's ok:false itself and returns a gateway failure envelope — an
in-band error can never appear in action output. The engine mechanism
survives on the mock pack ($.error) with its e2e moved to mod.rs;
the slack e2e now pins the gateway-failure surfacing instead.
Fixtures rewritten to the normalized shapes; all stubs speak the real
uniform envelope on the real endpoint. Docs follow. 224 open_connector
tests, 744 lib tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(spec): reconcile the 5.1 entry with the live-verified pack
The live-gateway fixes made issues pure issues (the OC action filters
out the PRs GitHub's raw endpoint mixes in) and removed the
pull_request marker column in favor of the
issues_declares_no_pull_request_marker negative-space guard, but the
5.1 entry still described the marker as nullable opaque JSON and
listed a 'pull_request IS NULL' end-to-end test that no longer exists.
Also refreshes per_page → perPage, the stub's real-envelope protocol,
the live-reconciliation status (fingerprint pins remain the open
follow-up), and the counts: 26 pack tests / 202 open_connector total,
with the counting command spelled out.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): add the admission gate's schema-mismatch fixture for github
The gate requires empty, null-bearing, nested, AND schema-mismatch
fixtures per pack; the mismatch case was covered only by engine-level
json_to_arrow tests, never at the pack contract level. A new
issues_type_mismatch.json page carries a valid first row and a second
row whose declared-UInt64 'number' arrives as a string; the contract
test pins the targeted error identity — column 'number', path
'$.number', page 1, row 1 (proving the error is row-scoped, not
page-wide), expected 'non-negative integer', found 'string' — and that
the offending value itself never appears. Spec entry 5.1 updated
(fixture list + 27 pack / 203 total counts).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): add the admission gate's schema-mismatch fixture for slack
Same gate clause as the github fixture: a
conversations_type_mismatch.json page whose second row carries
memberCount as a string where UInt64 is declared, with the contract
test pinning the targeted error identity (column member_count, path
$.memberCount, page 1, row 1, expected non-negative integer, found
string) and distinguishing it from the legitimate NULL path (an
OMITTED memberCount). Spec entry 5.2 updated (fixture list + 226
total).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(demo): call out that the raw-scan example is stub-only for now
The stub hardcodes execution.readOnly: true, so raw_issue_scan runs in
the demo — but today's real Open Connector publishes no read/write
classification, so open_connector_scan is refused live under
default-deny (RawActionReadOnlyUnknown). The stable-table and
open_connector_query examples degrade gracefully to the credential
wall; this one cannot, and the module docs were the only place saying
so. The demo now says it twice: a note beside the example itself and a
carries-over/doesn't list in the real-gateway section, including the
advice to drop raw_action_allowlist + the pipeline from a real-gateway
context.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): document the two layers handling in-band provider errors
Review asked what happens if a gateway forwards Slack's HTTP-200
ok:false envelope: both suggested remedies already exist — the engine's
error_path pre-check (checked before row extraction, mock-pack-modeled)
and the verified fact that Open Connector's slack executor consumes
ok:false and returns a failure envelope (assertSlackPayload throws, so
a conforming gateway can never emit 200+ok:false). What was missing was
saying so outside the slack doc: the generic error section now spells
out the two layers and the consequence of declaring neither, and the
exec.rs comment no longer implies the slack pack itself uses the
mechanism.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): restore env vars via RAII guard in the slack pack tests
The setup helper set the runtime-token variable with a bare unsafe
set_var/remove_var pair: a panic between the two leaked the variable
into later tests, and remove_var deletes rather than restores a
pre-existing value. testutil::EnvVarGuard now saves the prior state
(value or absence, as OsString) and restores it on drop — panic
included — and centralizes the unsafe with its soundness argument
(per-test-unique names; the guard adds restore-on-drop, not thread
safety). The equivalent call sites in mod.rs/github.rs/client.rs are
main-owned test code and migrate in a separate cleanup.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): pin the users cursor scan; refresh the spec's Review notes
Two live gaps from the review batch (the rest was fixed in earlier
rounds): the multi-page cursor path was pinned end-to-end only through
conversations — users shares the strategy constant but not the wire
declarations, so a drifted users declaration could hide behind
conversations' coverage. A two-page users scan now asserts row
identity across pages (U0001..U0003), no cursor on page 1, the stub's
token on page 2, and the limit 200 hint on every request. The spec's
Review notes still described the current PR as milestone 4; it now
describes 5.2 and the engine extensions it carried. Counts: 227
open_connector tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): fail on non-string cursors instead of truncating the scan
The Cursor advance arm collapsed every non-empty-string outcome of the
cursor extraction into 'no next page': nextCursor: 123, nextCursor: {},
and even structural traversal failures all read as end-of-collection,
so a drifted gateway silently truncated conversations/users scans while
the query succeeded. Termination is now ONLY the three end-of-collection
spellings — absent (any missing segment, matching Slack's omitted
response_metadata), null, or empty string. A present non-string cursor
fails as the new PaginationCursorInvalid (path, page, JSON kind — never
the value, mirroring PaginationTotalInvalid), and structural failures
(traversing through a non-object) propagate as themselves.
Tests: unit — null termination added to the missing/empty pin,
number/object/boolean cursors fail with their kinds, non-object parent
propagates RowPathNotObject while a missing parent still terminates;
e2e — a slack conversations scan against a nextCursor: 123 gateway
fails naming '$.nextCursor' and 'not a string' instead of returning
one page as success. 230 open_connector tests; guide + spec updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(sources): pin the slack action-contract fingerprints from the live gateway
The three slack tables carried expected_fingerprint: None, so the
registration-time compatibility gate never ran for them — upstream
output-schema drift would surface at scan time (or, combined with a
lenient cursor read, as silently missing data), against the design's
requirement that stable definitions record a fingerprint and fail at
registration. The deferral reason ('pins must come from a live
gateway') is gone: the contracts are live-reconciled, so the pins land.
Each pin is the BLAKE3 hash of the canonicalized output schema captured
from the live gateway (v1.3.1) into packs/fixtures/slack/contracts/.
Three layers keep it honest: a sync test locks pin ↔ captured contract
(fingerprint_schema is now pub(crate) for it); every mock registration
serves the captured contracts, so the gate's pass side is exercised by
the entire slack suite; and a drift e2e proves a gateway serving a
different schema fails registration as ActionContractMismatch naming
slack.conversations and its action — the gate's fail path was
previously dead code across all packs.
Docs spell out the pinning tradeoff (a hash cannot tell additive from
breaking, so any schema change fails until re-captured) and that the
github pack's pins remain the open follow-up. 232 open_connector tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(sources): add ValueFormat::Verbatim for non-timestamp mappings
Declaring Rfc3339 on string/number mappings (slack user_id, github
state x2, mock min_value, the filters test fixture) was a no-op only
because non-timestamp scalars happen to render identically under every
ValueFormat — semantics by coincidence, fragile if the rendering path
is ever reworked. Verbatim now states the intent, and carries real
semantics of its own: a timestamp literal reaching a Verbatim mapping
does NOT translate (returns None -> the predicate stays local) instead
of being pushed in a guessed spelling. Only the one genuine timestamp
mapping (github updated_at -> since) keeps Rfc3339.
New engine test pins both arms: plain scalars push their natural JSON
through Verbatim, a timestamp under Verbatim never reaches the wire
(Unsupported, evaluated by DataFusion). 233 open_connector / 790 full
lib tests; spec entry updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): state the default scan ceilings for the slack and github packs
With default bounds (max_pages 100), an unfiltered scan caps at
200 x 100 = 20k rows for the slack cursor tables, 100 x 100 = 10k for
slack files and every github table — and then FAILS with
ScanBoundsExceeded per the fail-don't-truncate rule, which is correct
but surprising the first time a large workspace/repo hits it. Both pack
docs now state the ceiling, the failure mode, and the remedies (raise
max_pages/max_rows in the open_connector block, or narrow with a
predicate/LIMIT), linking to the integration guide's bounds section.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(sources): define built-in source packs as embedded YAML assets
The design doc sketched packs as declarative YAML and explicitly left
YAML-vs-Rust as an implementation choice; this moves the implementation
to the design's illustrative format. mock, github, and slack are now
packs/*.yaml assets compiled in with include_str!, parsed once at first
registry access by the new packs/loader.rs, and leaked into the same
&'static shapes the scan engine has always borrowed — the engine,
bindings, UDTFs, fingerprint gate, and every scan-path type are
untouched.
The contract boundary does not move: packs stay inside the binary,
versioned, fingerprint-gated, and never user-editable configuration
(context YAML still cannot override an action, row path, pagination, or
schema). What changes is that a pack is now reviewable and generatable
as plain data, and the format is ready for the design's deferred second
tier (user-authored packs loaded from a directory).
Loader discipline mirrors the config layer: deny_unknown_fields end to
end (a typo'd pagination key fails parsing instead of silently
disabling the total it was meant to set), utf8_list_from_object_key is
the only column type that accepts 'key', table keys are bare names with
ids derived as <pack>.<table>, table order is BTreeMap-deterministic,
and filter 'format' defaults to verbatim (the safe spelling). A
malformed embedded asset is a build defect: the loader panics with the
asset name, and builtin_assets_parse_and_validate parses AND
structurally validates (row paths, converter, pagination, error paths)
every shipped asset so that panic is unreachable in a released binary.
The YAML was generated mechanically from the previous Rust statics and
is pinned by the existing suite: fingerprint sync tests, per-table
contract fixtures, and every e2e (pagination inputs, fixed-input pins,
filter pushdown, drift refusal) all pass unchanged. Pack modules keep
their module docs and tests; declaration-site rationale moved into YAML
comments. 237 open_connector / 794 full-lib tests green; zero new
clippy warnings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): validate pack semantics in the loader; fail without panicking
Three review findings on the YAML loader, all applied:
P1 - the loader now cross-validates each document before converting it
to runtime objects: duplicate column names (RowConverter does not
reject them), filters referencing undeclared columns, duplicate
(column, operator) mappings, resources declared both required and
optional, and fixed inputs colliding with declared resources or
pagination inputs. Structural checks (row/error paths, converter
construction, pagination paths) also moved into the parse pass so a
generated asset gets one complete diagnostic instead of failing
piecemeal at bind time. One table-driven test pins every rejection
with its targeted message.
P2 - a malformed embedded asset no longer panics: loader::builtin
memoizes the parse Result, pack()/SourcePackRegistry::builtins() are
fallible, and the error surfaces as the new SourcePackAssetInvalid at
registration (register_open_connector_tables) and UDTF setup
(register_open_connector_udtfs, now Result; server/CLI call sites
propagate). This matters precisely because the refactor exists to make
packs AI-generatable - a bad asset must be a startup diagnostic, not a
first-use abort.
P3 - source_pack.rs said 'parsed once at startup'; it is parsed at
first registry access, and the doc now says so, matching loader.rs.
238 open_connector / 795 skardi, 137 server, 63 CLI tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(sources): pin the github action-contract fingerprints from the live gateway
Closes the documented follow-up: github's 8 tables now pin
expected fingerprints the way slack's do. The output schemas were
captured from a live gateway into fixtures/github/contracts/, each
table's fingerprint in github.yaml is the BLAKE3 of its canonicalized
capture, and the same recipe applies end to end — a sync test locks
pin <-> contract through action_registry::fingerprint_schema (its
mismatch output is also how pins are re-taken after an upstream
upgrade), every e2e's discovery stub now serves the captured contracts
via github_discovery so the fingerprint gate's pass side runs
suite-wide, and a drift-refusal e2e pins the failure side (a differing
schema fails REGISTRATION naming github.issues,
github.list_repository_issues, and the fingerprint mismatch — never
silently reshaped rows mid-query).
Verified live: registration of all 8 github + 3 slack tables against a
local gateway passes the pinned comparison. Docs updated (github
Compatibility section, spec 5.1 follow-up closed). 240 open_connector
tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): fix intra-doc links and stale fingerprint prose in pack modules
The YAML refactor removed the pack modules' type imports, breaking the
module docs' bare intra-doc links under -D rustdoc::broken-intra-doc-links;
they now link by full path. Also refreshed the two module-doc blocks the
github fingerprint pinning made stale ('No fingerprint pins yet' /
'GitHub pack's pins remain a follow-up'). cargo doc clean, 797 lib
tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): state explicitly that embedded YAML is not dynamic loading
Review asked for the boundary to be explicit: the YAML refactor
standardizes the pack FORMAT, but adding a built-in pack still takes a
Rust accessor module, a mod declaration, a registry entry, and a
rebuild. Directory-loaded user packs remain the design's deliberately
deferred second tier (per the design doc, so the format can stabilize
while Skardi-internal); the loader doc now says so instead of implying
otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): validate the whole request-input namespace in the loader
Filter inputs join the same request-input namespace as resources, fixed
inputs, and pagination parameters — and exec.rs applies pagination
LAST, so a filter input named page/perPage/cursor would be silently
overwritten after translation: an Exact pushed predicate DataFusion
never reapplies, returning wrong rows. The loader now rejects filter
inputs colliding with pagination parameters or declared resources
(filter-vs-fixed-input overlap stays legal on purpose — a pushed
predicate overriding the complete-collection pin is the feature),
rejects a pagination declaration reusing one name for two parameters,
and rejects zero page sizes. Five new table-driven cases pin each
rejection's targeted message. 797 lib tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): reject two filter mappings sharing one input field
Follow-up to the namespace validation: two filters targeting the same
provider input are now rejected at load. The scan-time claimed-inputs
guard already made them safe (the later predicate stays local, so no
wrong rows) — but WHICH predicate pushes would depend on the query's
predicate order, and that ambiguity in a pack declaration is an
authoring mistake, not a choice. The other collision the review named
— a filter input equal to a fixed input — stays legal on purpose: it
is the override mechanism itself (a pushed predicate replacing the
complete-collection pin, the github state=all pattern, exercised
end-to-end by pull_requests_state_pin_and_override_run_end_to_end),
and the validation comment now spells out both decisions. One more
table-driven rejection case. 797 lib tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sources): reject non-finite fixed floats at pack parse
YAML parses .nan/.inf/-.inf into f64 and the untagged FixedValueDoc
preserved them, while FixedValue::to_json silently renders non-finite
numbers as JSON null at query time — exactly the pack bug the
FixedValue doc comment warns about, slipping past the startup
diagnostic the loader promises. convert_table now rejects a non-finite
fixed input during parsing, naming the table, the key, and the value;
three table-driven cases cover all three YAML spellings. 797 lib tests
green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(sources): drop stale 'github pins pending' prose
The github fingerprints landed with captured contracts under
fixtures/github/contracts/, but three sentences still described the
pinning as a pending follow-up: the integration guide's compatibility
section (the reviewed one) plus two passages in the tasks spec found by
sweeping for the same staleness. All three now state that both real
packs are pinned.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): handle register_open_connector_udtfs's Result in setup helpers
The function became fallible when pack loading stopped panicking, but
the three test setup helpers (slack, github, table_functions) still
ignored the Result, emitting unused_must_use in test builds. They now
expect() it, so a setup failure is explicit and the test build is
warning-free again.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(sources): pin each pack's fingerprint coverage gap
The fingerprint gate hashes the DECLARED output schema, and several
mapped columns exist only via additionalProperties passthrough (github
issues' created_at/updated_at/closed_at, most slack.files columns, more
across the github tables) — their drift is invisible to the gate and
surfaces at scan time instead. The captured contract cannot be widened
pack-side (the pin compares against live discovery; editing the capture
would fail registration), so the gap is made explicit instead of
implicit: a testutil helper walks every mapped path through the
captured row-item schema, and each pack pins its exact uncovered-column
set — conversations/users pin EMPTY sets (the normalizing executors
declare everything), proving the mechanism. Any change (upstream
declaring more, or a mapping change) now fails a test and forces a
conscious decision. The guide's compatibility section documents the
boundary and the scan-time consequences (shape change fails loudly, a
removed nullable field reads as NULL). 799 lib tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent 549f928 commit 33033a3
42 files changed
Lines changed: 4809 additions & 955 deletions
File tree
- crates
- cli/src
- server/src
- skardi/src/sources/providers/open_connector
- packs
- fixtures
- github/contracts
- slack
- contracts
- docs
- superpowers/specs
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
330 | 330 | | |
331 | 331 | | |
332 | 332 | | |
333 | | - | |
| 333 | + | |
334 | 334 | | |
335 | 335 | | |
336 | 336 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
483 | 483 | | |
484 | 484 | | |
485 | 485 | | |
486 | | - | |
| 486 | + | |
487 | 487 | | |
488 | 488 | | |
489 | 489 | | |
| |||
522 | 522 | | |
523 | 523 | | |
524 | 524 | | |
525 | | - | |
| 525 | + | |
526 | 526 | | |
527 | 527 | | |
528 | 528 | | |
| |||
551 | 551 | | |
552 | 552 | | |
553 | 553 | | |
554 | | - | |
| 554 | + | |
555 | 555 | | |
556 | 556 | | |
557 | 557 | | |
| |||
1237 | 1237 | | |
1238 | 1238 | | |
1239 | 1239 | | |
1240 | | - | |
| 1240 | + | |
1241 | 1241 | | |
1242 | 1242 | | |
1243 | 1243 | | |
| |||
1384 | 1384 | | |
1385 | 1385 | | |
1386 | 1386 | | |
1387 | | - | |
| 1387 | + | |
1388 | 1388 | | |
1389 | 1389 | | |
1390 | 1390 | | |
| |||
1542 | 1542 | | |
1543 | 1543 | | |
1544 | 1544 | | |
1545 | | - | |
| 1545 | + | |
1546 | 1546 | | |
1547 | 1547 | | |
1548 | 1548 | | |
| |||
2896 | 2896 | | |
2897 | 2897 | | |
2898 | 2898 | | |
2899 | | - | |
| 2899 | + | |
| 2900 | + | |
2900 | 2901 | | |
2901 | 2902 | | |
2902 | 2903 | | |
| |||
2914 | 2915 | | |
2915 | 2916 | | |
2916 | 2917 | | |
2917 | | - | |
| 2918 | + | |
| 2919 | + | |
2918 | 2920 | | |
2919 | 2921 | | |
2920 | 2922 | | |
| |||
2949 | 2951 | | |
2950 | 2952 | | |
2951 | 2953 | | |
2952 | | - | |
| 2954 | + | |
| 2955 | + | |
2953 | 2956 | | |
2954 | 2957 | | |
2955 | 2958 | | |
| |||
2970 | 2973 | | |
2971 | 2974 | | |
2972 | 2975 | | |
2973 | | - | |
| 2976 | + | |
| 2977 | + | |
2974 | 2978 | | |
2975 | 2979 | | |
2976 | 2980 | | |
| |||
3015 | 3019 | | |
3016 | 3020 | | |
3017 | 3021 | | |
3018 | | - | |
| 3022 | + | |
| 3023 | + | |
3019 | 3024 | | |
3020 | 3025 | | |
3021 | 3026 | | |
| |||
3031 | 3036 | | |
3032 | 3037 | | |
3033 | 3038 | | |
3034 | | - | |
| 3039 | + | |
| 3040 | + | |
3035 | 3041 | | |
3036 | 3042 | | |
3037 | 3043 | | |
| |||
3047 | 3053 | | |
3048 | 3054 | | |
3049 | 3055 | | |
3050 | | - | |
| 3056 | + | |
| 3057 | + | |
3051 | 3058 | | |
3052 | 3059 | | |
3053 | 3060 | | |
| |||
3065 | 3072 | | |
3066 | 3073 | | |
3067 | 3074 | | |
3068 | | - | |
| 3075 | + | |
| 3076 | + | |
3069 | 3077 | | |
3070 | 3078 | | |
3071 | 3079 | | |
| |||
3078 | 3086 | | |
3079 | 3087 | | |
3080 | 3088 | | |
3081 | | - | |
| 3089 | + | |
| 3090 | + | |
3082 | 3091 | | |
3083 | 3092 | | |
3084 | 3093 | | |
| |||
3096 | 3105 | | |
3097 | 3106 | | |
3098 | 3107 | | |
3099 | | - | |
| 3108 | + | |
| 3109 | + | |
3100 | 3110 | | |
3101 | 3111 | | |
3102 | 3112 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
123 | | - | |
| 123 | + | |
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
170 | 170 | | |
171 | 171 | | |
172 | 172 | | |
173 | | - | |
| 173 | + | |
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| |||
Lines changed: 46 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
207 | 247 | | |
208 | 248 | | |
209 | 249 | | |
| |||
354 | 394 | | |
355 | 395 | | |
356 | 396 | | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
357 | 403 | | |
358 | 404 | | |
359 | 405 | | |
| |||
Lines changed: 46 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
50 | 53 | | |
51 | 54 | | |
52 | 55 | | |
| |||
63 | 66 | | |
64 | 67 | | |
65 | 68 | | |
| 69 | + | |
66 | 70 | | |
67 | 71 | | |
68 | 72 | | |
| |||
274 | 278 | | |
275 | 279 | | |
276 | 280 | | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
277 | 284 | | |
278 | 285 | | |
279 | 286 | | |
| |||
358 | 365 | | |
359 | 366 | | |
360 | 367 | | |
| 368 | + | |
361 | 369 | | |
362 | 370 | | |
363 | 371 | | |
| |||
474 | 482 | | |
475 | 483 | | |
476 | 484 | | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
477 | 509 | | |
478 | 510 | | |
479 | 511 | | |
| |||
529 | 561 | | |
530 | 562 | | |
531 | 563 | | |
532 | | - | |
533 | | - | |
534 | | - | |
535 | | - | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
536 | 576 | | |
537 | 577 | | |
538 | 578 | | |
| |||
557 | 597 | | |
558 | 598 | | |
559 | 599 | | |
560 | | - | |
| 600 | + | |
561 | 601 | | |
562 | 602 | | |
563 | 603 | | |
| |||
571 | 611 | | |
572 | 612 | | |
573 | 613 | | |
574 | | - | |
| 614 | + | |
575 | 615 | | |
576 | 616 | | |
577 | 617 | | |
| |||
0 commit comments