Context: This plugin is for the Fivetran Connector SDK (CSDK). "CSDK" is shorthand for "Connector SDK".
Evaluate Connector
FIRST: Read sdk-reference.md from the plugin directory to load SDK rules and patterns.
Perform a static code evaluation of the connector. This is a read-only analysis — do NOT modify any files.
If no connector name is provided:
Ask which connector to evaluate. List any directories in the workspace that contain a connector.py file as options.
Step 1: Read Connector Code
Read all source files in the connector directory:
connector.py — required; main implementation
- Any other
.py files present
requirements.txt — if present, check for incorrectly declared pre-installed packages
Do NOT read or log any values from configuration.json.
If connector.py is missing, tell the user and stop.
Step 2: Evaluate
Analyze the code against the criteria below. Be deterministic and conservative — only flag issues with concrete code evidence. Do NOT flag theoretical or hypothetical problems.
CRITICAL SDK RULE — read before evaluating anything:
SDK operations (op.upsert, op.update, op.delete, op.checkpoint) must be called directly. They are NOT generators and must NEVER be used with yield or yield from.
- WRONG:
yield op.upsert(table="x", data=d)
- CORRECT:
op.upsert(table="x", data=d)
update() is a plain function, not a generator. yield from is only valid inside helper pagination functions that stream raw API records — it is never valid with SDK operations.
If you find code that calls SDK operations without yield, that is correct. Do not flag it.
REQUIRED Issues — Must Fix
Flag as required only when the code clearly demonstrates the problem.
1. Memory & Resource Management
- Entire dataset loaded into memory before processing (e.g., accumulating all records in a list before iterating)
- Files or connections opened without a context manager and without explicit
.close()
- Unbounded data structures that grow without limits
2. SDK Compliance
update(configuration, state) function must exist and be passed to Connector() instantiation
- At least one of
op.upsert(), op.update(), op.delete(), or op.truncate() must be called
op.checkpoint() must be called
- SDK operations (
op.upsert, op.update, op.delete, op.truncate, op.checkpoint) must be called directly — never with yield or yield from:
- WRONG:
yield op.upsert(table="x", data=d)
- CORRECT:
op.upsert(table="x", data=d)
update() must not return anything — SDK operations return None
- Schema: only
table, primary_key, columns keys are valid — any other key is an error
- Schema data types: if
columns are specified, only BOOLEAN, SHORT, INT, LONG, FLOAT, DOUBLE, DECIMAL, STRING, BINARY, JSON, XML, NAIVE_DATE, NAIVE_DATETIME, UTC_DATETIME are valid — any other type name is an error
- Declaring
columns with valid types is correct and supported — do NOT flag it as an issue. Declaring a primary_key for each table is recommended.
- Logging: preferred methods are
log.debug(), log.info(), log.warning(), log.error(), log.critical() — flag print(), logging.*, logger.* as required issues
- Type hints:
Generator[op.Operation, None, None] or any use of op.Operation in type hints is invalid — use plain dict and list only; never import from typing for SDK function signatures
exit() must never be used — use raise RuntimeError(...) instead
connector = Connector(...) must be at module (global) scope, not inside if __name__ == "__main__" or any function
3. Security
- Credentials, tokens, or secrets stored in the
state dict (state is persisted to disk unencrypted)
- Secrets or PII exposed in log messages (e.g.,
log.info(f"record: {data}"))
- Hardcoded credentials in source code
4. Data Reliability
- HTTP responses not validated — missing
raise_for_status() or equivalent status code check
- Infinite loops without a termination condition
- Missing pagination or streaming for API calls that return large datasets
- Cursor/state updated before processing the record (should be after):
- WRONG:
cursor = data['updated_at'] then op.upsert(...)
- CORRECT:
op.upsert(...) then cursor = data['updated_at']
5. Exception Handling
- Missing error handling around network, file, or database operations
- Exceptions caught but silently ignored (
except Exception: pass)
GOOD_TO_HAVE Issues — Suggestions
1. Performance
- HTTP requests missing a
timeout parameter
- Missing retry logic for transient network failures
2. Code Quality
- Functions over 50 lines without clear decomposition
- Missing input validation for required configuration keys
- Dead or duplicate code
requirements.txt lists requests or fivetran_connector_sdk — these are pre-installed in the runtime and must not be declared
- Schema declares a type for every column — declaring all columns forfeits the SDK's type inference and schema evolution. Prefer declaring types only where a specific type must be forced. (Declaring types for some columns is fine — do not flag that.)
- No
primary_key declared for a table — Fivetran will create a surrogate _fivetran_id key; declaring an explicit primary key is recommended
log.fine() or log.severe() used — these are deprecated Java-style aliases; prefer log.debug() and log.error() respectively
3. Reliability
- Retries without exponential backoff
- String timestamp comparison without datetime parsing (can fail across timezones)
- Pagination logic that could silently skip records
Do NOT Flag
- Code style or formatting preferences
- Theoretical edge cases not reachable in the actual execution path
- Issues already handled elsewhere in the code
- Cursor checkpoint placed after the loop when an empty page breaks before the cursor update — this is correct behavior
columns declared with valid data types — declaring types is explicitly supported by the SDK and useful for forcing a specific type. Only flag declaring a type for every column (good_to_have).
- Reading credentials from the
configuration dict — Fivetran encrypts configuration
- Any JSON-serializable value stored in state — all are valid
- Datetime string vs datetime object in
op.upsert() data — SDK accepts both
yield from inside a helper pagination generator that streams raw API records — this is correct and unrelated to SDK operations
log.fine() or log.severe() as a required issue — they are deprecated but still work; flag as good_to_have only
- Inline
ENCRYPTED:v1:<key_id>:local-fernet: values in configuration.json — this is normal; the plugin decrypts configuration values at runtime.
Step 3: Score
Start at 100 and deduct based on issues found:
Required deductions:
- Critical (security breach, data loss, SDK violation): −25 to −30 per issue
- Major (silent failures, memory exhaustion): −15 to −20 per issue
- Medium (reliability risk): −10 to −15 per issue
Good-to-have deductions:
- Significant omission: −3 to −5 per issue
- Minor suggestion: −1 to −2 per issue
Compute three subscores:
required_score: 100 minus required deductions
good_to_have_score: 100 minus good-to-have deductions
sdk_adherence_score: 100 minus SDK-specific violations only
Floor all scores at 0.
Step 4: Report
Present findings using this structure. Omit any section that has no issues.
## Evaluation Report — <connector_name>
### Score
Overall: <score>/100
- SDK Adherence: <sdk_adherence_score>/100
- Required: <required_score>/100
- Good to Have: <good_to_have_score>/100
### Required Issues
**[<tag>] <issue title>**
- Problem: <what is wrong>
- Location: <function name or line reference>
- Current code:
```python
<offending snippet>
Good to Have
Summary
<2–3 sentence overall assessment>
**Tags:** `memory management` | `security` | `resource management` | `reliability` | `exception handling` | `input validation` | `configurability` | `code quality` | `sdk compliance` | `others`
If no issues are found in a category, write `None found.`
Do NOT suggest fixes for `good_to_have` issues unless the fix is a straightforward one-liner. Do NOT modify any files.
1---2name: evaluate-connector3description: Evaluate a Fivetran connector for correctness, SDK compliance, security, and reliability. Use when the user wants a code review or quality report before deploying.4---56<!--7 GENERATED FILE — DO NOT EDIT.8 Canonical source: canonical/skills/evaluate-connector/SKILL.md9 Regenerate with: bash scripts/sync-plugins.sh10-->1112> **Context**: This plugin is for the Fivetran Connector SDK (CSDK). "CSDK" is shorthand for "Connector SDK".1314# Evaluate Connector1516**FIRST**: Read `sdk-reference.md` from the plugin directory to load SDK rules and patterns.1718Perform a static code evaluation of the connector. This is a read-only analysis — do NOT modify any files.1920**If no connector name is provided:**21Ask which connector to evaluate. List any directories in the workspace that contain a `connector.py` file as options.2223## Step 1: Read Connector Code2425Read all source files in the connector directory:26- `connector.py` — required; main implementation27- Any other `.py` files present28- `requirements.txt` — if present, check for incorrectly declared pre-installed packages2930Do NOT read or log any values from `configuration.json`.3132If `connector.py` is missing, tell the user and stop.3334## Step 2: Evaluate3536Analyze the code against the criteria below. Be deterministic and conservative — only flag issues with concrete code evidence. Do NOT flag theoretical or hypothetical problems.3738---3940> **CRITICAL SDK RULE — read before evaluating anything:**41> SDK operations (`op.upsert`, `op.update`, `op.delete`, `op.checkpoint`) must be called **directly**. They are NOT generators and must NEVER be used with `yield` or `yield from`.42> - WRONG: `yield op.upsert(table="x", data=d)`43> - CORRECT: `op.upsert(table="x", data=d)`44>45> `update()` is a plain function, not a generator. `yield from` is only valid inside helper pagination functions that stream raw API records — it is never valid with SDK operations.46>47> If you find code that calls SDK operations without `yield`, that is **correct**. Do not flag it.4849---5051### REQUIRED Issues — Must Fix5253Flag as `required` only when the code clearly demonstrates the problem.5455**1. Memory & Resource Management**56- Entire dataset loaded into memory before processing (e.g., accumulating all records in a list before iterating)57- Files or connections opened without a context manager and without explicit `.close()`58- Unbounded data structures that grow without limits5960**2. SDK Compliance**61- `update(configuration, state)` function must exist and be passed to `Connector()` instantiation62- At least one of `op.upsert()`, `op.update()`, `op.delete()`, or `op.truncate()` must be called63- `op.checkpoint()` must be called64- SDK operations (`op.upsert`, `op.update`, `op.delete`, `op.truncate`, `op.checkpoint`) must be called directly — never with `yield` or `yield from`:65 - WRONG: `yield op.upsert(table="x", data=d)`66 - CORRECT: `op.upsert(table="x", data=d)`67- `update()` must not return anything — SDK operations return `None`68- Schema: only `table`, `primary_key`, `columns` keys are valid — any other key is an error69- Schema data types: if `columns` are specified, only `BOOLEAN`, `SHORT`, `INT`, `LONG`, `FLOAT`, `DOUBLE`, `DECIMAL`, `STRING`, `BINARY`, `JSON`, `XML`, `NAIVE_DATE`, `NAIVE_DATETIME`, `UTC_DATETIME` are valid — any other type name is an error70- Declaring `columns` with valid types is **correct and supported** — do NOT flag it as an issue. Declaring a `primary_key` for each table is recommended.71- Logging: preferred methods are `log.debug()`, `log.info()`, `log.warning()`, `log.error()`, `log.critical()` — flag `print()`, `logging.*`, `logger.*` as required issues72- Type hints: `Generator[op.Operation, None, None]` or any use of `op.Operation` in type hints is invalid — use plain `dict` and `list` only; never import from `typing` for SDK function signatures73- `exit()` must never be used — use `raise RuntimeError(...)` instead74- `connector = Connector(...)` must be at module (global) scope, not inside `if __name__ == "__main__"` or any function7576**3. Security**77- Credentials, tokens, or secrets stored in the `state` dict (state is persisted to disk unencrypted)78- Secrets or PII exposed in log messages (e.g., `log.info(f"record: {data}")`)79- Hardcoded credentials in source code8081**4. Data Reliability**82- HTTP responses not validated — missing `raise_for_status()` or equivalent status code check83- Infinite loops without a termination condition84- Missing pagination or streaming for API calls that return large datasets85- Cursor/state updated **before** processing the record (should be after):86 - WRONG: `cursor = data['updated_at']` then `op.upsert(...)`87 - CORRECT: `op.upsert(...)` then `cursor = data['updated_at']`8889**5. Exception Handling**90- Missing error handling around network, file, or database operations91- Exceptions caught but silently ignored (`except Exception: pass`)9293---9495### GOOD_TO_HAVE Issues — Suggestions9697**1. Performance**98- HTTP requests missing a `timeout` parameter99- Missing retry logic for transient network failures100101**2. Code Quality**102- Functions over 50 lines without clear decomposition103- Missing input validation for required configuration keys104- Dead or duplicate code105- `requirements.txt` lists `requests` or `fivetran_connector_sdk` — these are pre-installed in the runtime and must not be declared106- Schema declares a type for **every** column — declaring all columns forfeits the SDK's type inference and schema evolution. Prefer declaring types only where a specific type must be forced. (Declaring types for *some* columns is fine — do not flag that.)107- No `primary_key` declared for a table — Fivetran will create a surrogate `_fivetran_id` key; declaring an explicit primary key is recommended108- `log.fine()` or `log.severe()` used — these are deprecated Java-style aliases; prefer `log.debug()` and `log.error()` respectively109110**3. Reliability**111- Retries without exponential backoff112- String timestamp comparison without datetime parsing (can fail across timezones)113- Pagination logic that could silently skip records114115---116117### Do NOT Flag118- Code style or formatting preferences119- Theoretical edge cases not reachable in the actual execution path120- Issues already handled elsewhere in the code121- Cursor checkpoint placed after the loop when an empty page breaks before the cursor update — this is correct behavior122- `columns` declared with valid data types — declaring types is explicitly supported by the SDK and useful for forcing a specific type. Only flag declaring a type for *every* column (good_to_have).123- Reading credentials from the `configuration` dict — Fivetran encrypts configuration124- Any JSON-serializable value stored in state — all are valid125- Datetime string vs datetime object in `op.upsert()` data — SDK accepts both126- `yield from` inside a helper pagination generator that streams raw API records — this is correct and unrelated to SDK operations127- `log.fine()` or `log.severe()` as a required issue — they are deprecated but still work; flag as good_to_have only128- Inline `ENCRYPTED:v1:<key_id>:local-fernet:` values in `configuration.json` — this is normal; the plugin decrypts configuration values at runtime.129130---131132## Step 3: Score133134Start at 100 and deduct based on issues found:135136**Required deductions:**137- Critical (security breach, data loss, SDK violation): −25 to −30 per issue138- Major (silent failures, memory exhaustion): −15 to −20 per issue139- Medium (reliability risk): −10 to −15 per issue140141**Good-to-have deductions:**142- Significant omission: −3 to −5 per issue143- Minor suggestion: −1 to −2 per issue144145Compute three subscores:146- `required_score`: 100 minus required deductions147- `good_to_have_score`: 100 minus good-to-have deductions148- `sdk_adherence_score`: 100 minus SDK-specific violations only149150Floor all scores at 0.151152---153154## Step 4: Report155156Present findings using this structure. Omit any section that has no issues.157158```159## Evaluation Report — <connector_name>160161### Score162Overall: <score>/100163- SDK Adherence: <sdk_adherence_score>/100164- Required: <required_score>/100165- Good to Have: <good_to_have_score>/100166167### Required Issues168**[<tag>] <issue title>**169- Problem: <what is wrong>170- Location: <function name or line reference>171- Current code:172 ```python173 <offending snippet>174 ```175- Fix:176 ```python177 <corrected snippet>178 ```179180### Good to Have181<same structure as above>182183### Summary184<2–3 sentence overall assessment>185```186187**Tags:** `memory management` | `security` | `resource management` | `reliability` | `exception handling` | `input validation` | `configurability` | `code quality` | `sdk compliance` | `others`188189If no issues are found in a category, write `None found.`190191Do NOT suggest fixes for `good_to_have` issues unless the fix is a straightforward one-liner. Do NOT modify any files.