Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .claude/skills/build-plugin/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ This skill **tests every data stream against a live, authenticated plugin** in y
1. Run `squaredup status --json`. If the command isn't found, install the CLI first: `npm i -g @squaredup/cli`. If it exits non-zero, the user is not logged in — ask them to run `! squaredup login` in this session, then re-run `squaredup status --json`. Capture the JSON output (`{ tenantName, region }`) — you'll need `region` in Checkpoint A. Login/region mechanics live in the `deploy-plugin` skill.
2. Confirm the user has a SquaredUp tenant where they can add and authenticate the plugin.

Checkpoint B drives the import with `squaredup index` / `index-status`, so a current `squaredup` CLI is assumed.
Checkpoint B drives the import with `squaredup index` / `index-status`, and confirms relationships with `squaredup correlate-status` / `edges`, so a current `squaredup` CLI is assumed — the correlation commands need **1.1.0 or later**. If `squaredup edges --help` reports an unknown command, the CLI is too old: `npm i -g @squaredup/cli`.

One of them needs more than a login: `squaredup correlate` re-runs rules on demand and requires a **tenant admin**, returning "requires a tenant admin" otherwise. `correlate-status` and `edges` are reads and need no such permission, so a non-admin can still confirm everything — they just can't re-run rules without a fresh import.

If login or a tenant is unavailable, **stop** — this skill cannot build a plugin it cannot test.

Expand Down Expand Up @@ -288,7 +290,7 @@ Author them inline in the main agent (they're small, and they depend on the impo
3. **Write the rule** — source is the side holding the foreign key; always write both `forward` and `reverse` labels; `"operator": "equals"` on every condition. Never write `pluginId`, `ruleType`, `schemaVersion`, or condition `id`s — those are stamped for you.
4. **Validate** — run `squaredup validate --json` from the plugin dir and confirm the summary's `Correlation Rules` count equals the number of files you wrote. An invalid rule fails the whole plugin validation, so fix any error before Checkpoint B.

Edges can't be confirmed until Checkpoint B's import has run — verification is a step there.
`validate` only proves the files are well-formed — it says nothing about whether they installed or matched anything. Both are confirmed after Checkpoint B's import, with `squaredup correlate-status` (did the rules install and run?) and `squaredup edges` (did they relate anything?). Verification is a step there.

---

Expand All @@ -297,10 +299,16 @@ Edges can't be confirmed until Checkpoint B's import has run — verification is
Scoped data streams can't be tested until objects exist, which means the import steps must be live and an import must have run. The CLI triggers and tracks the import for you, so **drive it yourself — don't ask the user to run it in the UI.**

1. **Redeploy** — invoke `deploy-plugin` again so the new import steps ship. The import definitions only take effect once this redeploy lands, so the import must run _after_ it.
2. **Trigger** — `squaredup index --datasource-id <id> --no-wait --json`. `--no-wait` returns immediately with a `since` anchor (capture it) instead of blocking until the import finishes — you poll for completion in the next step. (Plain `squaredup index` now waits and prints progress itself, which can outlast an agent command timeout on a long import; `--no-wait` is the orchestration path.) If an import was already running it reports `alreadyRunning: true` and adopts that run — poll with the `since` it returns either way.
2. **Trigger** — `squaredup index --datasource-id <id> --no-wait --json`. `--no-wait` returns immediately with a `since` anchor (capture it) instead of blocking until the import finishes — you poll for completion in the next step. (Plain `squaredup index` now waits and prints progress itself, which can outlast an agent command timeout on a long import; `--no-wait` is the orchestration path.) If an import was already running it reports `alreadyRunning: true` and adopts that run — poll with the `since` it returns either way. If Phase 5b shipped correlation rules, capture the **correlation** anchor before you trigger — see step 5.
3. **Wait** — poll `squaredup index-status --datasource-id <id> --since <since> --json` until `done` is `true`, passing the `since` from step 2. `succeeded: true` means objects are indexed; `succeeded: false` means the import failed — read the run-level `message` and the per-step `steps[]` (which step has `status: "failed"` and its `errorReason`) to pinpoint the break, fix that import stream, and re-trigger before continuing. Imports can take several minutes; use a generous timeout. See [checkpoints.md](references/checkpoints.md).
4. **Confirm** — check objects landed with an **inline scope**: `squaredup objects --matches '{"sourceType":{"type":"equals","value":"<Object Type>"}}' --plugin-id <pluginId> --datasource-id <id> --json` should return a non-empty list. `<Object Type>` is a `sourceType` from the `objectTypes` you defined in `metadata.json` / `indexDefinitions/default.json`. Use `--matches` here, **not** `objects <stream>`: that form resolves a data stream file's `matches`, but no scoped data stream exists yet (those come in Phase 6) and the import streams written so far have no `matches` to resolve. For the same reason, pass **inline** JSON — `--matches @<importStream>.json` won't work, as an import stream's `matches` is `none`/absent.
5. **Confirm relationships** — only if Phase 5b shipped correlation rules. Correlation is triggered automatically once the import succeeds, but fire-and-forget: it is _not_ part of the import status, so edges appear shortly **after** `index-status` reports done. The CLI has no edge-query command, so confirm in the tenant — ask the user to open an object of the source type and check its relationships — or use the SquaredUp MCP server's `graph_query` if one is connected. Zero edges from a rule that validated almost always means an unmapped join key or a `types` string that doesn't match `objectTypes`; see the common-mistakes table in [correlation-rules.md](references/correlation-rules.md).
5. **Confirm relationships** — only if Phase 5b shipped correlation rules. Correlation is triggered automatically once the import succeeds, but fire-and-forget: it is _not_ part of the import status, so edges appear shortly **after** `index-status` reports done. Drive the confirmation yourself:
- **Anchor first, before step 2's trigger.** `squaredup correlate-status --datasource-id <id> --json` and keep the highest `rules[].lastEvaluated` (`0` if the rules have never run). `--since <anchor>` is what makes `done` mean *this* run — without it, `done: true` can be the previous import's correlation, so a rule that matched last time reads as a pass before this run has even started. Don't reuse the `since` from step 2: that is the previous import's start, which the previous correlation already cleared.
- `squaredup correlate-status --datasource-id <id> --since <anchor> --json` — lists every rule this data source has installed, with its last-run outcome and `edgesCreated`. **The rule list is itself the check that your rules installed**: a rule you wrote that isn't listed never deployed. Poll until `done` is `true`, every ~5s, and **stop after ~5 minutes**.
- **On failure, stop here.** If the poll times out, or `succeeded` is `false`, or any rule reports `status: "failed"`, don't read `edges` — report the run-level outcome plus that rule's `ruleName` and `errorCount`, and fix it first. A timeout isn't a failure (the run continues server-side) but it isn't a confirmation either: say which rules were still in flight.
- `squaredup edges --datasource-id <id> --plugin-id <pluginId> --json` — the edges those rules actually produced. `--rule <ruleName>` narrows to one rule.

A rule reporting `edgesCreated: 0` names which rule to investigate — usually an unmapped join key or a `types` string that doesn't match `objectTypes`. Zero is the signal to look, not proof of a bug: a correct rule reports zero against data that holds nothing to relate, so check the join-key values actually pair up on both sides (`squaredup objects`) before rewriting the rule. See the common-mistakes table in [correlation-rules.md](references/correlation-rules.md), which also covers the fix loop.

### Re-indexing rule

Expand Down
37 changes: 36 additions & 1 deletion .claude/skills/build-plugin/references/checkpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,44 @@ Notes:
- **`status` is the run's lifecycle, not the outcome.** While running it's `ready`/`inProgress`; once `done` it's one of `succeeded`, `failed`, `warning`, or `cancelled`. `succeeded` and `warning` both report `succeeded: true` (a `warning` run finished but a step emitted warnings — check the `steps[]`); `failed` and `cancelled` report `succeeded: false`. A datasource that has never imported reports `status: "notRun"`, `done: false`.
- `--since` is **exclusive** (`scheduledStart > since`): always pass the `since` from `index` so `done` can't latch on a stale previous run.
- If `index` reports `alreadyRunning: true`, it adopted the in-flight run — poll with the `since` it returned. Imports can take several minutes (object import allows up to ~10 min); use a generous overall timeout.
- **Correlation runs after the import, not as part of it.** If the plugin ships `correlationRules/*.json`, a successful import triggers correlation fire-and-forget — `done: true, succeeded: true` says nothing about whether edges were written, and they land shortly afterwards. There is no CLI command for edges; confirm relationships in the tenant UI. See [correlation-rules.md](correlation-rules.md).
- **Correlation runs after the import, not as part of it.** If the plugin ships `correlationRules/*.json`, a successful import triggers correlation fire-and-forget — `done: true, succeeded: true` says nothing about whether edges were written, and they land shortly afterwards. Confirm them with the two commands below rather than in the UI.
- The `--matches` confirm in step 3 must be **inline JSON** — `--matches @<importStream>.json` only resolves a real scope, and an import stream's `matches` is `none`/absent. Likewise `objects <stream>` needs a scoped stream, which doesn't exist until Phase 6.

## Checkpoint B, step 5: confirm relationships

Only if the plugin ships correlation rules. Correlation lands after the import, so poll for it — with an anchor, the same way you poll `index-status`:

```bash
# 0. BEFORE triggering the import (step 2): read the anchor — the newest lastEvaluated across the rules.
squaredup correlate-status --datasource-id <id> --json --silent
# → { "rules": [ { "ruleName": "relate-pod-to-node", "lastEvaluated": 1755500000000, ... } ] }
# anchor = max(rules[].lastEvaluated), or 0 if the rules have never run.

# 1. After index-status reports done: poll until THIS run's correlation is done.
squaredup correlate-status --datasource-id <id> --since <anchor> --json --silent
# → { "done": true, "succeeded": true,
# "rules": [ { "ruleName": "relate-pod-to-node", "status": "succeeded",
# "done": true, "succeeded": true, "edgesCreated": 42,
# "verticesProcessed": 50, "errorCount": 0, "limitExceeded": false,
# "lastEvaluated": 1755500123456 } ] }

# 2. The edges themselves. --rule <ruleName> narrows to one rule.
squaredup edges --datasource-id <id> --plugin-id <pluginId> --json --silent
# → { "edges": [ { "source": {...}, "target": {...}, "label": "runs on", "origin": "plugin" } ], "truncated": false }
```

Notes:

- **`--since` is what makes `done` mean *this* run.** It's an exclusive anchor on `lastEvaluated`, exactly like `index-status --since` on `scheduledStart`. Omit it and `done: true` can be the *previous* import's correlation — a rule that ran and matched last time reads as a pass before this run has even started. Don't reuse the `since` from `index`: that is the previous import's start time, which the previous correlation run already cleared. Take the anchor from a pre-import `correlate-status`, or from `squaredup correlate --no-wait --json`, which prints one.
- **Bound the poll.** Poll every ~5s and give up after ~5 minutes (the CLI's own `correlate` polls at 5s and waits 600s by default). A timeout is **not** a failure — the run continues server-side — but it is also not a confirmation: report the relationships as unconfirmed and say which rules were still in flight, rather than reading `edges` and treating whatever is there as the result.
- **Stop before `edges` on a failure.** If `succeeded` is `false`, or any rule reports `status: "failed"`, report the run-level outcome plus the offending rule's `ruleName`, `status` and `errorCount`, and fix that before reading edges. `succeeded` is `null` while any rule is still in flight; a rule that has never been evaluated reports `status: "notRun"`, `done: false`. `status: "warnings"` counts as succeeded — the rule created edges but logged errors, so check `errorCount`.
- **The `rules` list is how you confirm the rules installed.** A `correlationRules/*.json` you wrote that doesn't appear never deployed. `validate` only proves the file parses.
- **Read per rule, not just the top-level flags.** `succeeded: true` means every rule ran, not that any matched — a rule with `edgesCreated: 0` and a healthy `verticesProcessed` ran fine and matched nothing. That is usually a rule bug, but a correct rule reports zero on data that holds nothing to relate, so check the join-key values actually pair up on both sides (`squaredup objects`) before rewriting it. See the common-mistakes table in [correlation-rules.md](correlation-rules.md).
- `limitExceeded: true` means the rule stopped at the tenant edge limit, so `edgesCreated` is a floor rather than the full match count.
- `edges` needs `--plugin-id` as well as `--datasource-id`; `correlate-status` needs only the datasource.
- **An empty `edges` result is exit 0, not an error** — poll it the same way you poll `index-status`, rather than treating the first empty read as failure.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- To re-run rules without a fresh import (the authoring loop after fixing a rule), use `squaredup correlate` — **tenant admin only**. Without admin, trigger another import instead. See [correlation-rules.md](correlation-rules.md).

## Imported objects are frozen at import time — re-index to refresh them

The objects this import created carry the shape defined by the `indexDefinitions/*.json` and import streams **as they were when the import ran**. Editing either afterwards does **not** retroactively change the objects already in the graph — every existing object stays **stale**, and a newly mapped property is absent on it until the datasource is re-imported. This is the trap behind the shipped `undefined === undefined` scope bug: a property was added to `objectMapping.properties` *after* the import, sub-agents were told it existed, and they filtered on a field that was `undefined` on every object.
Expand Down
Loading
Loading