From 4682dbb3f12dfe983e01bfd3cb1141929b1783c3 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 19:01:51 +0000 Subject: [PATCH 01/15] CXF-221: verify / update-rollback / diagnose skills (PR 6) Co-authored-by: c1-squire-dev[bot] --- evals/README.md | 3 +- evals/runner/agent.test.ts | 2 +- evals/runner/scenario.test.ts | 2 +- evals/runner/skills_bundle.test.ts | 43 ++++++++- evals/scenarios/pre1-directory-proceed.json | 2 +- evals/scenarios/pre1-noiam-park.json | 2 +- evals/scenarios/tier1-directory-full.json | 2 +- evals/skills-bundle/README.md | 18 +++- evals/skills-bundle/bundle.json | 7 +- skills/README.md | 12 ++- skills/diagnose-authoring-failure/SKILL.md | 89 ++++++++++++++++++ skills/diagnose-authoring-failure/SOURCES.md | 13 +++ skills/update-and-rollback/SKILL.md | 98 ++++++++++++++++++++ skills/update-and-rollback/SOURCES.md | 11 +++ skills/verify-connector-output/SKILL.md | 63 +++++++++++++ skills/verify-connector-output/SOURCES.md | 12 +++ 16 files changed, 361 insertions(+), 18 deletions(-) create mode 100644 skills/diagnose-authoring-failure/SKILL.md create mode 100644 skills/diagnose-authoring-failure/SOURCES.md create mode 100644 skills/update-and-rollback/SKILL.md create mode 100644 skills/update-and-rollback/SOURCES.md create mode 100644 skills/verify-connector-output/SKILL.md create mode 100644 skills/verify-connector-output/SOURCES.md diff --git a/evals/README.md b/evals/README.md index cb85a52..42644ea 100644 --- a/evals/README.md +++ b/evals/README.md @@ -15,7 +15,7 @@ fixture and produces a scored JSONL record with the full S0–S11 stage funnel. | `evals/runner/` | Runner + scorer (`run.ts` CLI, driver interfaces, stage gates) | | `evals/runner/drivers/` | Driver implementations — Tier-0 local/static driver; authoring contract in `drivers/README.md` | | `evals/scenarios/` | Scenario definitions (`tier1-directory.json`, `tier1-directory-guide-only.json`, `tier1-directory-full.json`, `pre1-directory-proceed.json`, `pre1-noiam-park.json`) | -| `evals/skills-bundle/` | Skill-bundle mount point (v0.3.0 manifest — seven skills in `skills/`) | +| `evals/skills-bundle/` | Skill-bundle mount point (v0.4.0 manifest — ten skills in `skills/`) | | `evals/results/` | JSONL run records (gitignored; `.gitkeep` committed) | ## How to run @@ -242,7 +242,6 @@ real-tenant driver and the tool surface are available. ## Non-goals -- The remaining three skills (verify-connector-output, update-and-rollback, diagnose-authoring-failure) — later PRs. - Tier-2 real sandbox providers and the qualitative LLM-judge tier. - Operator-side activation E2E leg (redeeming the approval token) — those two fields are `skipped_human_boundary`. diff --git a/evals/runner/agent.test.ts b/evals/runner/agent.test.ts index 3665551..7fceeb8 100644 --- a/evals/runner/agent.test.ts +++ b/evals/runner/agent.test.ts @@ -78,7 +78,7 @@ test("buildPrompt pre1 branch names the pre1Path, both skills, and the output co basicAuth: {username: "connector@example.com", password: "fixture-token"}, bearerToken: "fixture-token", }, - skillBundle: {mode: "full", version: "0.3.0"}, + skillBundle: {mode: "full", version: "0.4.0"}, model: "together/deepseek-ai/DeepSeek-V4-Flash-0731", reasoningEffort: "high", kind: "pre1", diff --git a/evals/runner/scenario.test.ts b/evals/runner/scenario.test.ts index 66d14c9..c5f741e 100644 --- a/evals/runner/scenario.test.ts +++ b/evals/runner/scenario.test.ts @@ -100,7 +100,7 @@ test("loadScenario loads the pre1-directory-proceed scenario (kind pre1, proceed assert.equal(s.seed, undefined) assert.equal(s.requiredSourceFiles, undefined) assert.equal(s.skillBundle.mode, "full") - assert.equal(s.skillBundle.version, "0.3.0") + assert.equal(s.skillBundle.version, "0.4.0") }) test("loadScenario loads the pre1-noiam-park scenario (kind pre1, park)", () => { diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 735bb16..24feac8 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -14,8 +14,8 @@ import {loadScenario} from "./scenario.ts" const execFileAsync = promisify(execFile) const RUN = "evals/runner/run.ts" const BUNDLE = "evals/skills-bundle/bundle.json" -const SKILLS = ["author-in-app-connector", "read-authoring-contract", "write-connector-source", "build-and-test", "deploy-and-activate", "design-access-model", "source-openapi-spec"] -const VERSION = "0.3.0" +const SKILLS = ["author-in-app-connector", "read-authoring-contract", "write-connector-source", "build-and-test", "deploy-and-activate", "design-access-model", "source-openapi-spec", "verify-connector-output", "update-and-rollback", "diagnose-authoring-failure"] +const VERSION = "0.4.0" function readBundle(): {version: string; skills: {name: string; version: string; path: string}[]} { return JSON.parse(readFileSync(BUNDLE, "utf8")) as {version: string; skills: {name: string; version: string; path: string}[]} @@ -56,7 +56,7 @@ test("(a) each SKILL.md exists with the locked frontmatter contract", () => { test("(b) every bundle.json path resolves to an existing file", () => { const bundle = readBundle() assert.equal(bundle.version, VERSION) - assert.equal(bundle.skills.length, 7) + assert.equal(bundle.skills.length, 10) assert.deepEqual(bundle.skills.map((s) => s.name), SKILLS) const skillsRoot = resolve("skills") for (const skill of bundle.skills) { @@ -133,6 +133,39 @@ const SKILL_LITERALS: Record = { "Parking with evidence", "authority ladder", ], + "verify-connector-output": [ + "never invent data to make a demo appear complete", + "SYNC_STATUS_DONE", + "sync_disabled", + "every grant principal references an emitted resource", + "entitlement ID references an emitted entitlement", + "ID stability", + "/admin/connector/", + ], + "update-and-rollback": [ + "serve image does not match the revision-pinned runtime image", + "/api/v1/connector-authoring/rollbacks", + "target_revision_id", + "approval_token_id", + "activation_epoch", + "image digest", + "Do not redeem the approval token", + ], + "diagnose-authoring-failure": [ + "262144 byte compile limit", + "1048576 byte limit", + "is_secret", + "credential re-entry required", + "missing type", + "unregistered transport", + "ticketing.enabled must be true when ticketing is configured", + "activation evidence is unsatisfied", + "Invalid token provided", + "ConnectionOK", + "HostCallOK", + "c1_connector_service_get", + "status.lastError", + ], } test("(c) each SKILL.md carries the locked section markers, content literals, ASCII-only bodies, and stays <= 200 lines", () => { @@ -152,7 +185,7 @@ test("(c) each SKILL.md carries the locked section markers, content literals, AS test("(d) the full-mode scenario parses with mode full and the two pinned scenarios keep their locked modes", () => { const full = loadScenario("evals/scenarios/tier1-directory-full.json") assert.equal(full.skillBundle.mode, "full") - assert.equal(full.skillBundle.version, "0.3.0") + assert.equal(full.skillBundle.version, "0.4.0") assert.equal(full.id, "tier1-directory-full") const none = loadScenario("evals/scenarios/tier1-directory.json") assert.equal(none.skillBundle.mode, "none") @@ -201,7 +234,7 @@ test("(e) CLI end-to-end: full-mode Tier-0 run exits 0 and the record meta carri const lines = readFileSync(join(dir, records[0]), "utf8").trim().split("\n") const meta = JSON.parse(lines[0]) as Record assert.equal(meta.skill_bundle_mode, "full") - assert.equal(meta.skill_bundle_version, "0.3.0") + assert.equal(meta.skill_bundle_version, "0.4.0") } finally { rmSync(dir, {recursive: true, force: true}) } diff --git a/evals/scenarios/pre1-directory-proceed.json b/evals/scenarios/pre1-directory-proceed.json index 52fa477..e7e522a 100644 --- a/evals/scenarios/pre1-directory-proceed.json +++ b/evals/scenarios/pre1-directory-proceed.json @@ -29,7 +29,7 @@ }, "skillBundle": { "mode": "full", - "version": "0.3.0" + "version": "0.4.0" }, "model": "together/deepseek-ai/DeepSeek-V4-Flash-0731", "reasoningEffort": "high" diff --git a/evals/scenarios/pre1-noiam-park.json b/evals/scenarios/pre1-noiam-park.json index c90bada..2bc6f20 100644 --- a/evals/scenarios/pre1-noiam-park.json +++ b/evals/scenarios/pre1-noiam-park.json @@ -27,7 +27,7 @@ }, "skillBundle": { "mode": "full", - "version": "0.3.0" + "version": "0.4.0" }, "model": "together/deepseek-ai/DeepSeek-V4-Flash-0731", "reasoningEffort": "high" diff --git a/evals/scenarios/tier1-directory-full.json b/evals/scenarios/tier1-directory-full.json index f5f111e..5b4b6a2 100644 --- a/evals/scenarios/tier1-directory-full.json +++ b/evals/scenarios/tier1-directory-full.json @@ -27,7 +27,7 @@ }, "skillBundle": { "mode": "full", - "version": "0.3.0" + "version": "0.4.0" }, "model": "together/deepseek-ai/DeepSeek-V4-Flash-0731", "reasoningEffort": "high", diff --git a/evals/skills-bundle/README.md b/evals/skills-bundle/README.md index 0fe1f6b..d701b25 100644 --- a/evals/skills-bundle/README.md +++ b/evals/skills-bundle/README.md @@ -2,6 +2,22 @@ This directory is the skill-bundle mount point for the eval harness. +## v0.4.0 - the ten skills + +The bundle ships ten skills: the seven prior skills plus three post-funnel +and cross-cutting skills at `0.1.0`: + +- `verify-connector-output` - post-11: post-sync verification (counts, + grant wiring, ID stability across re-sync, UI spot-check). +- `update-and-rollback` - post-activation: same-catalog update flow with + the image-digest reuse rule, the rotation STOP/escalate limitation, and + REST-only OWNER-gated rollback. +- `diagnose-authoring-failure` - cross-cutting: symptom -> cause -> fix + router over the common-failures table, the draft-test FAIL reading, and + where logs live. + +The seven prior skills are unchanged; the bundle version is `0.4.0`. + ## v0.3.0 — the seven skills The bundle ships seven skills: the five funnel skills plus two new pre-1 @@ -43,4 +59,4 @@ names a skill and a `path` relative to this directory pointing into `skills/` (the canonical home). Private drivers mount per the manifest — the manifest indirection is the contract, not a directory copy. The scenario file selects the bundle via `skillBundle.mode` (`full`) and pins -`skillBundle.version` (`0.2.0`); the runner records both in every run record. +`skillBundle.version` (`0.4.0`); the runner records both in every run record. diff --git a/evals/skills-bundle/bundle.json b/evals/skills-bundle/bundle.json index 1815d6b..b312544 100644 --- a/evals/skills-bundle/bundle.json +++ b/evals/skills-bundle/bundle.json @@ -1,5 +1,5 @@ { - "version": "0.3.0", + "version": "0.4.0", "skills": [ {"name": "author-in-app-connector", "version": "0.2.1", "path": "../../skills/author-in-app-connector/SKILL.md"}, {"name": "read-authoring-contract", "version": "0.2.0", "path": "../../skills/read-authoring-contract/SKILL.md"}, @@ -7,6 +7,9 @@ {"name": "build-and-test", "version": "0.2.0", "path": "../../skills/build-and-test/SKILL.md"}, {"name": "deploy-and-activate", "version": "0.2.0", "path": "../../skills/deploy-and-activate/SKILL.md"}, {"name": "design-access-model", "version": "0.1.0", "path": "../../skills/design-access-model/SKILL.md"}, - {"name": "source-openapi-spec", "version": "0.1.0", "path": "../../skills/source-openapi-spec/SKILL.md"} + {"name": "source-openapi-spec", "version": "0.1.0", "path": "../../skills/source-openapi-spec/SKILL.md"}, + {"name": "verify-connector-output", "version": "0.1.0", "path": "../../skills/verify-connector-output/SKILL.md"}, + {"name": "update-and-rollback", "version": "0.1.0", "path": "../../skills/update-and-rollback/SKILL.md"}, + {"name": "diagnose-authoring-failure", "version": "0.1.0", "path": "../../skills/diagnose-authoring-failure/SKILL.md"} ] } diff --git a/skills/README.md b/skills/README.md index e58368c..5d7f750 100644 --- a/skills/README.md +++ b/skills/README.md @@ -1,8 +1,11 @@ # Agent skills -The seven skills shipped in this batch, authored against -the v0.0.26 DSL contract and the 23-tool tenant MCP surface. Each skill's -`SOURCES.md` names the pinned sources with their SHAs. +The ten skills shipped in this batch: the seven funnel skills authored +against the v0.0.26 DSL contract and the 23-tool tenant MCP surface, plus +three post-funnel and cross-cutting skills (`verify-connector-output`, +`update-and-rollback`, `diagnose-authoring-failure`) over the tenant +connector and authoring tool surface. Each skill's `SOURCES.md` names the +pinned sources with their SHAs. | Skill | Stage coverage | Source basis | |---|---|---| @@ -13,6 +16,9 @@ the v0.0.26 DSL contract and the 23-tool tenant MCP surface. Each skill's | `deploy-and-activate` | Stages 6–8, 11 — app, provision, configure, deploy, mint, handoff | MCP-served guide, `authoring.proto`, lifecycle doc | | `design-access-model` | Pre-1 — access-model design for net-new providers | baton-admin `design-baton-access-model` @ `6fe6886f…` | | `source-openapi-spec` | Pre-1 — OpenAPI spec sourcing + IAM go/no-go | claude-marketplace `source-openapi-spec` @ `0cc5ac2a…` | +| `verify-connector-output` | Post-11 - post-sync verification (counts, grant wiring, ID stability, UI spot-check) | MCP-served guide, lifecycle doc, `probe-contracts.md` @ `0cc5ac2a…` | +| `update-and-rollback` | Post-activation - same-catalog update + REST rollback | MCP-served guide, `authoring.proto`, lifecycle doc | +| `diagnose-authoring-failure` | Cross-cutting - symptom -> cause -> fix router | lifecycle doc, baton-admin `diagnose-connector-failure` @ `6fe6886f…` | The eval bundle (`evals/skills-bundle/bundle.json`) is a manifest pointing into this directory; the skill bodies live here as the single source of diff --git a/skills/diagnose-authoring-failure/SKILL.md b/skills/diagnose-authoring-failure/SKILL.md new file mode 100644 index 0000000..2c054e0 --- /dev/null +++ b/skills/diagnose-authoring-failure/SKILL.md @@ -0,0 +1,89 @@ +--- +name: diagnose-authoring-failure +description: Use when a build, draft test, activation, or production sync fails and you need the symptom-to-cause-to-fix route. Do not use when the connector is healthy and you are verifying sync output - use verify-connector-output; do not use when updating or rolling back a live connector - use update-and-rollback. +version: 0.1.0 +--- + +# diagnose-authoring-failure + +Symptom -> cause -> fix router for authored-connector failures. Built on the +lifecycle doc's common-failures table, the draft-test evidence reading, and +where logs live. Ports the taxonomy approach of baton-admin +`diagnose-connector-failure`; replaces all repo tooling. + +## Workflow + +1. Start from the symptom: the exact error text, the failing step (build, + draft test, activation, production sync), and the IDs at hand. +2. Route the symptom through the table below; collect the smallest evidence + needed for that cause. +3. Apply the fix, then re-run the smallest verification step that failed. +4. Output a chat diagnostic summary: failure summary, classified cause, + evidence used, likely owner (source, schema, runtime, credentials, or + platform), one concrete next fix, and the smallest verification command. + +## Symptom -> cause -> fix + +| Symptom | Cause / fix | +|---------|-------------| +| Build rejected: `connector source exceeds the 262144 byte compile limit` | The esbuild-bundled source is over 256 KiB. Trim the source set; large OpenAPI-derived spec assets are the usual weight. | +| Build rejected: `connector bundle with embedded runtime specs is bytes, above the 1048576 byte limit` | The bundled `connector.js` plus its embedded runtime specs is over 1 MiB - a separate cap from the 256 KiB source limit, hit after bundling. Trim the source or the generated spec assets it embeds. | +| Build rejected: `credential-class config field must be marked is_secret` | A token/secret/password-named field in `runtime-schema.json` lacks `is_secret: true`. The `secret:` spelling is not read. | +| Draft test: `credential re-entry required: ` | Configure the instance credentials before the draft test. | +| Draft test: `connector config field X is missing type` | Add `"type": "string"` (etc.) to the field in `runtime.config_schema`. | +| Sync error mentions an `unregistered transport` | A `node` or `reuse` references a transport missing from `connector({ transports: ... })`. Register that same transport object, rebuild, and retest. | +| Draft test: `ticketing.enabled must be true when ticketing is configured` | The runtime-schema carries a `ticketing` block the connector code does not back. Remove the block (and any `actions` / `policy_surface` entries referencing dropped code); `enabled: false` is not a valid off-switch. | +| Activation reports `activation evidence is unsatisfied` | No PASS test-sync evidence binds this revision. Run the draft test (with credentials set) and confirm it passed before asking the OWNER to review a fresh approval URL. | +| Production sync `Invalid token provided` | The API token is wrong or truncated. Re-configure with the full token, then re-run. | + +## Draft-test FAIL reading + +The evidence row is authoritative: poll +`c1_connector_authoring_get_test_run_evidence` and read the `result` (PASS +or FAIL) and the `error` field. The FAIL reason lives on the evidence row; +it is not always logged when the read activity succeeds but the outcome +evaluation returns FAIL. PASS requires all of: + +- `ConnectionOK` - Validate succeeded +- `HostCallOK` - GetMetadata succeeded +- No read error and no write attempt +- The config version handle matches the candidate revision +- The runtime image digest matches the revision-pinned image + +A FAIL row (or no row yet) means activation stays `evidence is unsatisfied` +until a new draft test writes PASS. + +## Where logs live + +Use the product's connector activity and sync logs - the same surface you +use to view any connector in your tenant. There is no separate +operator-only log surface. The connector's status row +(`c1_connector_service_get` -> `status.status`, `status.lastError`) is the +authoritative outcome for a sync. + +## Exit criteria + +- Every one of the nine common-failures rows routes to its documented fix. +- A draft-test FAIL is read from the evidence row, not from completion. +- The logs location and the status row are named. +- The body contains the literals `262144 byte compile limit`, + `1048576 byte limit`, `is_secret`, `credential re-entry required`, + `missing type`, `unregistered transport`, + `ticketing.enabled must be true when ticketing is configured`, + `activation evidence is unsatisfied`, `Invalid token provided`, + `ConnectionOK`, `HostCallOK`, `c1_connector_service_get`, and + `status.lastError`. + +## Anti-patterns + +- Do not start with broad full-suite runs when a small probe can isolate + the issue. +- Do not hide unresolved drift with waivers or mock shaping. +- Do not expose auth material, tokens, or customer data in diagnostics. +- Do not guess a fix without reading the evidence row first. + +## Blocker protocol + +If the same validation or runtime error remains unchanged after 2 failed +fix cycles on the same error, stop and report the exact error text instead +of guessing further. diff --git a/skills/diagnose-authoring-failure/SOURCES.md b/skills/diagnose-authoring-failure/SOURCES.md new file mode 100644 index 0000000..8871c61 --- /dev/null +++ b/skills/diagnose-authoring-failure/SOURCES.md @@ -0,0 +1,13 @@ +# Sources - diagnose-authoring-failure + +Authored against the pinned sources below (decision 5: nothing written from +model memory). Source-of-truth precedence: (a) MCP-served guide, (b) +`authoring.proto`, (c) lifecycle doc, (e) baton-admin +`diagnose-connector-failure`. + +| Source | Pin / SHA | What this skill quotes | +|---|---|---| +| MCP-served guide | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The Caps table (262144-byte compile limit, 1048576-byte bundle limit) and the Evidence and credentials contract (`credential re-entry required`; activation fails closed until a PASS evidence row binds the revision digests). | +| Authoring proto | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | `GetTestRunEvidence` (poll `(catalog_id, revision_id, test_run_id)`; `result` PASS/FAIL + `error` on the evidence row). | +| Lifecycle doc | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The Debugging section: the common-failures table (all nine rows), the draft-test FAIL reading (evidence row authoritative; PASS requires `ConnectionOK`, `HostCallOK`, no read error and no write attempt, config version handle match, runtime image digest match), and where logs live (product connector activity and sync logs; the status row `c1_connector_service_get` -> `status.status`, `status.lastError`). | +| baton-admin `diagnose-connector-failure` | `6fe6886f607ed0d2e48a616c30e7ce4bffc32489` | The taxonomy approach: start from the symptom, classify the failure surface, collect the smallest evidence, keep the diagnosis focused on symptom/evidence/owner/next-fix/rerun-target, and the output contract (failure summary, classified surface, evidence used, likely owner, one concrete next fix, smallest verification command). | diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md new file mode 100644 index 0000000..586cc48 --- /dev/null +++ b/skills/update-and-rollback/SKILL.md @@ -0,0 +1,98 @@ +--- +name: update-and-rollback +description: Use when shipping a change to an activated connector (same-catalog rerun) or rolling back to a previously activated revision. Do not use when verifying a healthy connector's sync output - use verify-connector-output; do not use when diagnosing a failed build, draft test, or sync - use diagnose-authoring-failure. +version: 0.1.0 +--- + +# update-and-rollback + +Update and rollback for an activated authored connector. Runs in a +post-activation session - never during the funnel run. Tool names below are +the exact tenant MCP titles. + +## Update flow (same-catalog rerun) + +1. Reuse the existing `catalog_id` and `draft_id`; update the draft source + (lifecycle step 2) and build a new revision (steps 4-5). +2. Reuse the existing managed runtime instance ONLY when its image digest + matches the target revision's pinned runtime image digest. GATE: image + digest match. STOP if the digests differ - see the rotation limitation + below. +3. Pass a fresh draft test with the instance credentials (steps 9-10). + GATE: durable PASS evidence binds the new revision. +4. Mint a new approval URL with `c1_connector_authoring_mint_approval_token` + (`expires_in_seconds` 1-14400). GATE: non-empty `activation_url`. +5. HARD STOP at the human boundary: present the URL to a human tenant OWNER + and stop. Do not redeem the approval token. +6. After the OWNER activates, poll + `c1_connector_authoring_list_revision_summaries` until the target + revision is `REVISION_STATUS_ACTIVE`; record its `activation_epoch`. + GATE: ACTIVE. STOP if not ACTIVE after ~10 polls. +7. Call `c1_connector_service_force_sync`; verify via + `c1_connector_service_get` that `status.status` is `SYNC_STATUS_DONE`. + +## Rotation STOP (known limitation) + +An active update can fail with `serve image does not match the revision-pinned runtime image`. The deployed instance's image digest must +match the target revision's pinned runtime image digest. There is no +supported customer, MCP, or Support Dashboard recovery today: deploy and +teardown are pre-activation-only, and rollback enforces the same image +binding. + +**STOP.** Do not clear runtime fields, call the provisioner directly, or +mutate the deployment or AWS resources. Record the tenant, catalog, app, +connector, and target revision IDs, then escalate to Connector Authoring / +managed-runtime engineering. + +## Rollback (REST-only, OWNER-gated) + +To roll back to a previously activated revision, mint an approval token for +the rollback target revision and redeem it against the rollback endpoint - +unlike activation, rollback is REST-only and OWNER-gated. Use the product +base URL and OWNER bearer token: + +``` +POST /api/v1/connector-authoring/rollbacks +{ + "catalog_id": "", + "target_revision_id": "", + "instance_app_id": "", + "instance_connector_id": "", + "approval_token_id": "" +} +``` + +The rollback re-points the published and instance pointers at the target +revision under a strictly greater activation epoch. The rolled-back-from +revision's serve state is untouched - the pointer move alone stops it +serving. + +## Exit criteria + +- Same-catalog rerun: new revision ACTIVE with a recorded `activation_epoch`, + fresh PASS evidence, and a completed force sync. +- The rotation STOP fired verbatim (`serve image does not match the revision-pinned runtime image`) with the escalation record: tenant, + catalog, app, connector, and target revision IDs. +- Rollback: `POST /api/v1/connector-authoring/rollbacks` accepted with + `target_revision_id`, `instance_app_id`, `instance_connector_id`, and + `approval_token_id`; the target revision is ACTIVE under a strictly + greater activation epoch. +- The body contains the literals `serve image does not match the revision-pinned runtime image`, `/api/v1/connector-authoring/rollbacks`, + `target_revision_id`, `approval_token_id`, `activation_epoch`, and + `image digest`. + +## Anti-patterns + +- Do not redeem the approval token - activation is a human OWNER step. +- Do not reuse the managed runtime instance when the image digest does not + match the target revision's pinned runtime image digest. +- Do not clear runtime fields, call the provisioner directly, or mutate the + deployment or AWS resources on the rotation STOP. +- Do not roll back via the activation endpoint - rollback is REST-only. +- Do not force-sync before the target revision is ACTIVE. + +## Blocker protocol + +If the same validation or runtime error remains unchanged after 2 failed +fix cycles on the same error, stop and report the exact error text instead +of guessing further. diff --git a/skills/update-and-rollback/SOURCES.md b/skills/update-and-rollback/SOURCES.md new file mode 100644 index 0000000..9eb77f2 --- /dev/null +++ b/skills/update-and-rollback/SOURCES.md @@ -0,0 +1,11 @@ +# Sources - update-and-rollback + +Authored against the pinned sources below (decision 5: nothing written from +model memory). Source-of-truth precedence: (a) MCP-served guide, (b) +`authoring.proto`, (c) lifecycle doc. + +| Source | Pin / SHA | What this skill quotes | +|---|---|---| +| MCP-served guide | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The "Updating an active connector" contract: reuse the existing managed runtime instance, update the draft source, build a new revision, fresh PASS evidence, mint a new approval URL, human OWNER activates, poll `list_revision_summaries` until ACTIVE, record `activation_epoch`, force sync. | +| Authoring proto | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | `MintApprovalToken` (`expires_in_seconds` 1-14400, `activation_url`); `ListRevisionSummaries` + `RevisionStatus` enum (`REVISION_STATUS_ACTIVE` = 1, `activation_epoch` on the ACTIVE row); `RollbackRevision` (OWNER role, `POST /api/v1/connector-authoring/rollbacks`, request fields `catalog_id` / `target_revision_id` / `instance_app_id` / `instance_connector_id` / `approval_token_id`). | +| Lifecycle doc | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The "Updating and rolling back a live connector" section: the image-digest reuse rule; the rotation STOP verbatim (`serve image does not match the revision-pinned runtime image`; do not clear runtime fields, call the provisioner directly, or mutate the deployment or AWS resources; record tenant/catalog/app/connector/target-revision IDs; escalate to Connector Authoring / managed-runtime engineering); the REST-only OWNER-gated rollback body and the strictly-greater-activation-epoch pointer move. | diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md new file mode 100644 index 0000000..615e2a4 --- /dev/null +++ b/skills/verify-connector-output/SKILL.md @@ -0,0 +1,63 @@ +--- +name: verify-connector-output +description: Use when verifying a post-activation connector's sync output: resource/entitlement/grant counts, grant wiring, ID stability across re-sync, and the UI spot-check. Do not use when updating or rolling back a live connector - use update-and-rollback; do not use when diagnosing a failed build, draft test, or sync - use diagnose-authoring-failure. +version: 0.1.0 +--- + +# verify-connector-output + +Post-sync verification for an activated authored connector. Runs in a +post-activation session - never during the funnel run. Tool names below are +the exact tenant MCP titles. + +## Checklist + +1. Read the connector: call `c1_connector_service_get`; confirm + `status.status` is `SYNC_STATUS_DONE` (a subsequent `sync_disabled` is + normal for authored connectors). GATE: DONE. STOP if the status row + reports an error - read `status.lastError` and route through + diagnose-authoring-failure. +2. Counts: inspect the resource, entitlement, and grant counts for the + intended scope. Assert count parity against the live tenant API, not + against a seed list - the live product may own extra rows (default + users, bootstrap objects). GATE: counts match the intended scope. +3. Grant wiring: verify that every grant principal references an emitted resource + and every grant entitlement ID references an emitted entitlement. GATE: no + dangling principal or entitlement ID. +4. ID stability: re-sync and confirm the emitted resource and entitlement + IDs are stable across the re-sync - no churn in identity fields. + GATE: IDs unchanged. +5. UI spot-check: open `/admin/connector///` + on the product base URL and confirm the connector's resources and grants + appear. GATE: resources and grants visible. + +Investigate empty or unexpected results; never invent data to make a demo appear complete. + +## Exit criteria + +- `c1_connector_service_get` reports `SYNC_STATUS_DONE` (a subsequent + `sync_disabled` is normal). +- Resource, entitlement, and grant counts match the intended scope. +- Every grant principal references an emitted resource; every grant + entitlement ID references an emitted entitlement. +- IDs are stable across a re-sync. +- The UI spot-check at `/admin/connector///` + shows the connector's resources and grants. +- The body contains the literals `SYNC_STATUS_DONE`, `sync_disabled`, + `ID stability`, and `/admin/connector/`. + +## Anti-patterns + +- Never invent data to make a demo appear complete - investigate empty or + unexpected results instead. +- Do not assert fixture counts as the expected counts; query the live API + for count parity. +- Do not run this during the funnel run - it is a post-activation session + skill. +- Do not skip the grant-wiring check because counts look right. + +## Blocker protocol + +If the same validation or runtime error remains unchanged after 2 failed +fix cycles on the same error, stop and report the exact error text instead +of guessing further. diff --git a/skills/verify-connector-output/SOURCES.md b/skills/verify-connector-output/SOURCES.md new file mode 100644 index 0000000..01585c5 --- /dev/null +++ b/skills/verify-connector-output/SOURCES.md @@ -0,0 +1,12 @@ +# Sources - verify-connector-output + +Authored against the pinned sources below (decision 5: nothing written from +model memory). Source-of-truth precedence: (a) MCP-served guide, (b) +`authoring.proto`, (c) lifecycle doc, (d) marketplace `probe-contracts.md`. + +| Source | Pin / SHA | What this skill quotes | +|---|---|---| +| MCP-served guide | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The Production-sync verification contract verbatim: inspect resource/entitlement/grant counts for the intended scope; every grant principal references an emitted resource; every grant entitlement ID references an emitted entitlement; "Investigate empty or unexpected results; never invent data to make a demo appear complete". | +| Authoring proto | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The authoring RPC surface the post-activation session must not call during verification (the funnel tools; verification uses the tenant connector tools). | +| Lifecycle doc | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | "What success looks like": `SYNC_STATUS_DONE` via `c1_connector_service_get` (a subsequent `sync_disabled` is normal); the UI spot-check path `/admin/connector///`. | +| Marketplace `probe-contracts.md` | claude-marketplace `0cc5ac2a2dbe60b430444c59e53016da2c72b3d1` | The assertion-inventory adaptations: per-resource-type sync counts; count parity against the live API, not the seed list; ID stability across re-sync. | From bf27e9fad0c28cc4cc85cd15d160f14b7457522e Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 19:20:11 +0000 Subject: [PATCH 02/15] =?UTF-8?q?CXF-221:=20review=20fixes=20=E2=80=94=20c?= =?UTF-8?q?orrect=20rollback=20provenance,=20harden=20polling=20and=20cred?= =?UTF-8?q?ential=20guidance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update-and-rollback/SOURCES.md: drop the fabricated RollbackRevision proto RPC citation (the proto exposes no rollback RPC; the REST-only contract lives in the lifecycle doc) — restores decision-5 provenance integrity. - update-and-rollback/SKILL.md: add poll-with-backoff + evidence-unsatisfied guardrail to the ACTIVE poll (mirrors deploy-and-activate); add OWNER bearer-token hygiene anti-pattern. - diagnose-authoring-failure/SKILL.md: bound the evidence polling with backoff and a ~10-poll stop. - verify-connector-output/SKILL.md: handle RUNNING/unknown sync status in the status gate. - skills_bundle.test.ts: lock the rotation-STOP safety negation literal; add test (f) asserting every skill ships a non-empty SOURCES.md and the three new skills name the c1 pin. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 18 ++++++++++++++++++ skills/diagnose-authoring-failure/SKILL.md | 7 ++++--- skills/update-and-rollback/SKILL.md | 8 +++++++- skills/update-and-rollback/SOURCES.md | 2 +- skills/verify-connector-output/SKILL.md | 4 +++- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 24feac8..3866d6a 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -150,6 +150,7 @@ const SKILL_LITERALS: Record = { "activation_epoch", "image digest", "Do not redeem the approval token", + "Do not clear runtime fields, call the provisioner directly, or mutate the", ], "diagnose-authoring-failure": [ "262144 byte compile limit", @@ -239,3 +240,20 @@ test("(e) CLI end-to-end: full-mode Tier-0 run exits 0 and the record meta carri rmSync(dir, {recursive: true, force: true}) } }) + +test("(f) every skill ships a non-empty SOURCES.md naming its pinned sources", () => { + const skillsRoot = resolve("skills") + for (const name of SKILLS) { + const sourcesPath = join(skillsRoot, name, "SOURCES.md") + assert.ok(existsSync(sourcesPath), `missing SOURCES.md: ${name}`) + const content = readFileSync(sourcesPath, "utf8") + assert.ok(content.length > 0, `empty SOURCES.md: ${name}`) + } + // The three new skills must name the c1 pin (decision 5: nothing written + // from model memory) so a dropped or truncated pin fails the gate. + const c1Pin = "2e5f53eb441a93087d9754085ca17a5061e125ea" + for (const name of ["verify-connector-output", "update-and-rollback", "diagnose-authoring-failure"]) { + const content = readFileSync(join(skillsRoot, name, "SOURCES.md"), "utf8") + assert.ok(content.includes(c1Pin), `${name}: SOURCES.md does not name the c1 pin`) + } +}) diff --git a/skills/diagnose-authoring-failure/SKILL.md b/skills/diagnose-authoring-failure/SKILL.md index 2c054e0..50a3d45 100644 --- a/skills/diagnose-authoring-failure/SKILL.md +++ b/skills/diagnose-authoring-failure/SKILL.md @@ -40,9 +40,10 @@ where logs live. Ports the taxonomy approach of baton-admin The evidence row is authoritative: poll `c1_connector_authoring_get_test_run_evidence` and read the `result` (PASS -or FAIL) and the `error` field. The FAIL reason lives on the evidence row; -it is not always logged when the read activity succeeds but the outcome -evaluation returns FAIL. PASS requires all of: +or FAIL) and the `error` field. Poll with backoff (e.g. every 5-10s); if no +row after ~10 polls, stop and report `NotFound`/pending. The FAIL reason +lives on the evidence row; it is not always logged when the read activity +succeeds but the outcome evaluation returns FAIL. PASS requires all of: - `ConnectionOK` - Validate succeeded - `HostCallOK` - GetMetadata succeeded diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index 586cc48..1960405 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -27,7 +27,10 @@ the exact tenant MCP titles. 6. After the OWNER activates, poll `c1_connector_authoring_list_revision_summaries` until the target revision is `REVISION_STATUS_ACTIVE`; record its `activation_epoch`. - GATE: ACTIVE. STOP if not ACTIVE after ~10 polls. + GATE: ACTIVE. STOP if not ACTIVE - if approval reports `evidence is + unsatisfied`, return to the draft-test step and confirm a fresh PASS row + binds this revision before minting a new approval URL. Poll with backoff + (e.g. every 5-10s); if no ACTIVE row after ~10 polls, STOP and report. 7. Call `c1_connector_service_force_sync`; verify via `c1_connector_service_get` that `status.status` is `SYNC_STATUS_DONE`. @@ -90,6 +93,9 @@ serving. deployment or AWS resources on the rotation STOP. - Do not roll back via the activation endpoint - rollback is REST-only. - Do not force-sync before the target revision is ACTIVE. +- Do not print, log, or otherwise expose the OWNER bearer token value - + reference it only by variable or placeholder in any command or + diagnostic output. ## Blocker protocol diff --git a/skills/update-and-rollback/SOURCES.md b/skills/update-and-rollback/SOURCES.md index 9eb77f2..799d892 100644 --- a/skills/update-and-rollback/SOURCES.md +++ b/skills/update-and-rollback/SOURCES.md @@ -7,5 +7,5 @@ model memory). Source-of-truth precedence: (a) MCP-served guide, (b) | Source | Pin / SHA | What this skill quotes | |---|---|---| | MCP-served guide | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The "Updating an active connector" contract: reuse the existing managed runtime instance, update the draft source, build a new revision, fresh PASS evidence, mint a new approval URL, human OWNER activates, poll `list_revision_summaries` until ACTIVE, record `activation_epoch`, force sync. | -| Authoring proto | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | `MintApprovalToken` (`expires_in_seconds` 1-14400, `activation_url`); `ListRevisionSummaries` + `RevisionStatus` enum (`REVISION_STATUS_ACTIVE` = 1, `activation_epoch` on the ACTIVE row); `RollbackRevision` (OWNER role, `POST /api/v1/connector-authoring/rollbacks`, request fields `catalog_id` / `target_revision_id` / `instance_app_id` / `instance_connector_id` / `approval_token_id`). | +| Authoring proto | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | `MintApprovalToken` (`expires_in_seconds` 1-14400, `activation_url`); `ListRevisionSummaries` + `RevisionStatus` enum (`REVISION_STATUS_ACTIVE` = 1, `activation_epoch` on the ACTIVE row). The proto exposes no rollback RPC - its service comment notes rollback is the REST-only endpoint; the rollback contract lives in the lifecycle doc row below. | | Lifecycle doc | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The "Updating and rolling back a live connector" section: the image-digest reuse rule; the rotation STOP verbatim (`serve image does not match the revision-pinned runtime image`; do not clear runtime fields, call the provisioner directly, or mutate the deployment or AWS resources; record tenant/catalog/app/connector/target-revision IDs; escalate to Connector Authoring / managed-runtime engineering); the REST-only OWNER-gated rollback body and the strictly-greater-activation-epoch pointer move. | diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index 615e2a4..6011510 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -16,7 +16,9 @@ the exact tenant MCP titles. `status.status` is `SYNC_STATUS_DONE` (a subsequent `sync_disabled` is normal for authored connectors). GATE: DONE. STOP if the status row reports an error - read `status.lastError` and route through - diagnose-authoring-failure. + diagnose-authoring-failure. If `status.status` is `RUNNING`, poll with + backoff (e.g. every 5-10s) until DONE or ERROR; an unknown status value + routes through diagnose-authoring-failure. 2. Counts: inspect the resource, entitlement, and grant counts for the intended scope. Assert count parity against the live tenant API, not against a seed list - the live product may own extra rows (default From f8dcfb1bf23d2ccc75992bcd2db687e546192941 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 19:33:27 +0000 Subject: [PATCH 03/15] CXF-221: bound the verify RUNNING poll, handle DISABLED, lock new safety prose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - verify-connector-output/SKILL.md: the RUNNING-status poll now stops after ~10 polls (STOP and report) instead of polling unbounded; SYNC_STATUS_DISABLED is called out as normal (per the sync_disabled caveat) instead of being caught by the unknown-status catch-all. - skills_bundle.test.ts: lock the new safety prose in SKILL_LITERALS — SYNC_STATUS_RUNNING (verify), evidence is unsatisfied (update-and-rollback), Poll with backoff (diagnose-authoring-failure). Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 3 +++ skills/verify-connector-output/SKILL.md | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 3866d6a..d14f95b 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -136,6 +136,7 @@ const SKILL_LITERALS: Record = { "verify-connector-output": [ "never invent data to make a demo appear complete", "SYNC_STATUS_DONE", + "SYNC_STATUS_RUNNING", "sync_disabled", "every grant principal references an emitted resource", "entitlement ID references an emitted entitlement", @@ -151,6 +152,7 @@ const SKILL_LITERALS: Record = { "image digest", "Do not redeem the approval token", "Do not clear runtime fields, call the provisioner directly, or mutate the", + "evidence is unsatisfied", ], "diagnose-authoring-failure": [ "262144 byte compile limit", @@ -164,6 +166,7 @@ const SKILL_LITERALS: Record = { "Invalid token provided", "ConnectionOK", "HostCallOK", + "Poll with backoff", "c1_connector_service_get", "status.lastError", ], diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index 6011510..30fc370 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -16,9 +16,11 @@ the exact tenant MCP titles. `status.status` is `SYNC_STATUS_DONE` (a subsequent `sync_disabled` is normal for authored connectors). GATE: DONE. STOP if the status row reports an error - read `status.lastError` and route through - diagnose-authoring-failure. If `status.status` is `RUNNING`, poll with - backoff (e.g. every 5-10s) until DONE or ERROR; an unknown status value - routes through diagnose-authoring-failure. + diagnose-authoring-failure. If `status.status` is `SYNC_STATUS_RUNNING`, + poll with backoff (e.g. every 5-10s) until DONE or ERROR; if no + DONE/ERROR after ~10 polls, STOP and report. `SYNC_STATUS_DISABLED` is + normal (see above); any other unexpected status value routes through + diagnose-authoring-failure. 2. Counts: inspect the resource, entitlement, and grant counts for the intended scope. Assert count parity against the live tenant API, not against a seed list - the live product may own extra rows (default From 3ff569766528542f8e0871989fbe3d86f5062a50 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 19:41:49 +0000 Subject: [PATCH 04/15] CXF-221: rewrap step 6 so the evidence-unsatisfied literal is contiguous The f8dcfb1 literal lock for update-and-rollback ('evidence is unsatisfied') was added against a working-tree rewrap that was never committed; the pushed branch still wrapped the phrase across lines and failed test (c) on a clean checkout. Commit the rewrap so the locked literal is present verbatim. Co-authored-by: c1-squire-dev[bot] --- skills/update-and-rollback/SKILL.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index 1960405..0bdf02f 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -27,10 +27,10 @@ the exact tenant MCP titles. 6. After the OWNER activates, poll `c1_connector_authoring_list_revision_summaries` until the target revision is `REVISION_STATUS_ACTIVE`; record its `activation_epoch`. - GATE: ACTIVE. STOP if not ACTIVE - if approval reports `evidence is - unsatisfied`, return to the draft-test step and confirm a fresh PASS row - binds this revision before minting a new approval URL. Poll with backoff - (e.g. every 5-10s); if no ACTIVE row after ~10 polls, STOP and report. + GATE: ACTIVE. STOP if not ACTIVE - if approval reports `evidence is unsatisfied`, + return to the draft-test step and confirm a fresh PASS row binds this + revision before minting a new approval URL. Poll with backoff (e.g. every + 5-10s); if no ACTIVE row after ~10 polls, STOP and report. 7. Call `c1_connector_service_force_sync`; verify via `c1_connector_service_get` that `status.status` is `SYNC_STATUS_DONE`. From af466a0dde049c58dfb68b95773801b953d90b1d Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 19:49:31 +0000 Subject: [PATCH 05/15] CXF-221: DISABLED is not unconditionally normal; lock the poll bounds - verify-connector-output/SKILL.md: SYNC_STATUS_DISABLED is normal only for the post-activation sync_disabled state; a DISABLED row with status.lastError set (e.g. a data-anomaly auto-pause) routes through diagnose-authoring-failure. The backend derives DISABLED only from an ERROR-classified sync recategorized as ops pause / customer opt-out / data-anomaly (ConnectorStatusToAPI), so the blanket 'normal' carve-out would silently certify an anomaly-paused connector as verified. - skills_bundle.test.ts: lock the poll-termination bounds themselves ('DONE/ERROR after ~10 polls, STOP and report', 'row after ~10 polls, stop and report') and the data-anomaly distinction, so a future edit cannot strip the bound clauses while the suite stays green. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 3 +++ skills/verify-connector-output/SKILL.md | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index d14f95b..fa1d29e 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -137,6 +137,8 @@ const SKILL_LITERALS: Record = { "never invent data to make a demo appear complete", "SYNC_STATUS_DONE", "SYNC_STATUS_RUNNING", + "DONE/ERROR after ~10 polls, STOP and report", + "data-anomaly auto-pause", "sync_disabled", "every grant principal references an emitted resource", "entitlement ID references an emitted entitlement", @@ -167,6 +169,7 @@ const SKILL_LITERALS: Record = { "ConnectionOK", "HostCallOK", "Poll with backoff", + "row after ~10 polls, stop and report", "c1_connector_service_get", "status.lastError", ], diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index 30fc370..1f53ee7 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -19,8 +19,10 @@ the exact tenant MCP titles. diagnose-authoring-failure. If `status.status` is `SYNC_STATUS_RUNNING`, poll with backoff (e.g. every 5-10s) until DONE or ERROR; if no DONE/ERROR after ~10 polls, STOP and report. `SYNC_STATUS_DISABLED` is - normal (see above); any other unexpected status value routes through - diagnose-authoring-failure. + normal only for the post-activation `sync_disabled` state; a DISABLED + row with `status.lastError` set (e.g. a data-anomaly auto-pause) routes + through diagnose-authoring-failure. Any other unexpected status value + routes through diagnose-authoring-failure. 2. Counts: inspect the resource, entitlement, and grant counts for the intended scope. Assert count parity against the live tenant API, not against a seed list - the live product may own extra rows (default From bf3d4f24228d49ded487c763666dd9efdaaf5ef6 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 20:10:20 +0000 Subject: [PATCH 06/15] CXF-221: key the DISABLED handling off sync_disabled_category; lock remaining safety prose - verify-connector-output/SKILL.md: SYNC_STATUS_DISABLED is an error-classified outcome; the discriminator is now the connector's sync_disabled_category, not status.lastError presence (every real DISABLED row carries a non-empty lastError via ConnectorSyncStatusSetError, so the old gate misrouted benign ops/customer pauses). A data-anomaly auto-pause (SYNC_DISABLED_CATEGORY_DATA_ANOMALY) means the sync was paused after repeated data drops - investigate the counts and the drop reason; a deliberate pause (customer opt-out, ops, or deployment) is normal. - skills_bundle.test.ts: lock SYNC_DISABLED_CATEGORY_DATA_ANOMALY (verify), the ACTIVE-poll bound and the OWNER bearer-token hygiene bullet (update-and-rollback). Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 3 +++ skills/verify-connector-output/SKILL.md | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index fa1d29e..0f2aa88 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -139,6 +139,7 @@ const SKILL_LITERALS: Record = { "SYNC_STATUS_RUNNING", "DONE/ERROR after ~10 polls, STOP and report", "data-anomaly auto-pause", + "SYNC_DISABLED_CATEGORY_DATA_ANOMALY", "sync_disabled", "every grant principal references an emitted resource", "entitlement ID references an emitted entitlement", @@ -155,6 +156,8 @@ const SKILL_LITERALS: Record = { "Do not redeem the approval token", "Do not clear runtime fields, call the provisioner directly, or mutate the", "evidence is unsatisfied", + "if no ACTIVE row after ~10 polls, STOP and report", + "Do not print, log, or otherwise expose the OWNER bearer token value", ], "diagnose-authoring-failure": [ "262144 byte compile limit", diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index 1f53ee7..3f9cc5d 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -19,10 +19,12 @@ the exact tenant MCP titles. diagnose-authoring-failure. If `status.status` is `SYNC_STATUS_RUNNING`, poll with backoff (e.g. every 5-10s) until DONE or ERROR; if no DONE/ERROR after ~10 polls, STOP and report. `SYNC_STATUS_DISABLED` is - normal only for the post-activation `sync_disabled` state; a DISABLED - row with `status.lastError` set (e.g. a data-anomaly auto-pause) routes - through diagnose-authoring-failure. Any other unexpected status value - routes through diagnose-authoring-failure. + an error-classified outcome: read the connector's `sync_disabled_category`. + A data-anomaly auto-pause (`SYNC_DISABLED_CATEGORY_DATA_ANOMALY`) means + the sync was paused after repeated data drops - investigate the counts + below and the drop reason; a deliberate pause (customer opt-out, ops, or + deployment) is normal. Any other unexpected status value routes through + diagnose-authoring-failure. 2. Counts: inspect the resource, entitlement, and grant counts for the intended scope. Assert count parity against the live tenant API, not against a seed list - the live product may own extra rows (default From 067ae19cf79a03b6e320f84e24b2a935265eae89 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 20:23:59 +0000 Subject: [PATCH 07/15] CXF-221: key the DISABLED discriminator on sync_disabled_reason (populated field) - verify-connector-output/SKILL.md: sync_disabled_category is never populated on the app_v1.Connector returned by c1_connector_service_get (mdapp.ConnectorToAPI omits it), so the round-5 discriminator was unreadable and every real DISABLED row fell through to the unknown-status branch. Key the discriminator on sync_disabled_reason instead, which IS populated: the data-anomaly auto-pause stamps the 'Sync paused due to significant drop in sync data' prefix (SyncPausedPrefix), while deliberate pauses use 'system' / 'system-customer-opt-out'. Drop the unreachable 'deployment' category (ConnectorStatusToAPI promotes only OPERATIONS/CUSTOMER_OPT_OUT/DATA_ANOMALY to DISABLED). - verify-connector-output/SOURCES.md: provenance row for the DISABLED semantics from the c1 Go source at the same pin. - skills_bundle.test.ts: lock 'significant drop in sync data' instead of the unreadable category enum. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 2 +- skills/verify-connector-output/SKILL.md | 12 ++++++------ skills/verify-connector-output/SOURCES.md | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 0f2aa88..133af23 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -139,7 +139,7 @@ const SKILL_LITERALS: Record = { "SYNC_STATUS_RUNNING", "DONE/ERROR after ~10 polls, STOP and report", "data-anomaly auto-pause", - "SYNC_DISABLED_CATEGORY_DATA_ANOMALY", + "significant drop in sync data", "sync_disabled", "every grant principal references an emitted resource", "entitlement ID references an emitted entitlement", diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index 3f9cc5d..e9d10e1 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -19,12 +19,12 @@ the exact tenant MCP titles. diagnose-authoring-failure. If `status.status` is `SYNC_STATUS_RUNNING`, poll with backoff (e.g. every 5-10s) until DONE or ERROR; if no DONE/ERROR after ~10 polls, STOP and report. `SYNC_STATUS_DISABLED` is - an error-classified outcome: read the connector's `sync_disabled_category`. - A data-anomaly auto-pause (`SYNC_DISABLED_CATEGORY_DATA_ANOMALY`) means - the sync was paused after repeated data drops - investigate the counts - below and the drop reason; a deliberate pause (customer opt-out, ops, or - deployment) is normal. Any other unexpected status value routes through - diagnose-authoring-failure. + an error-classified outcome: read the connector's `sync_disabled_reason`. + A data-anomaly auto-pause (reason starting `Sync paused due to + significant drop in sync data`) means the sync was paused after repeated + data drops - investigate the counts below and the drop reason; a + deliberate pause (customer opt-out or ops) is normal. Any other + unexpected status value routes through diagnose-authoring-failure. 2. Counts: inspect the resource, entitlement, and grant counts for the intended scope. Assert count parity against the live tenant API, not against a seed list - the live product may own extra rows (default diff --git a/skills/verify-connector-output/SOURCES.md b/skills/verify-connector-output/SOURCES.md index 01585c5..1dc5aec 100644 --- a/skills/verify-connector-output/SOURCES.md +++ b/skills/verify-connector-output/SOURCES.md @@ -10,3 +10,4 @@ model memory). Source-of-truth precedence: (a) MCP-served guide, (b) | Authoring proto | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The authoring RPC surface the post-activation session must not call during verification (the funnel tools; verification uses the tenant connector tools). | | Lifecycle doc | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | "What success looks like": `SYNC_STATUS_DONE` via `c1_connector_service_get` (a subsequent `sync_disabled` is normal); the UI spot-check path `/admin/connector///`. | | Marketplace `probe-contracts.md` | claude-marketplace `0cc5ac2a2dbe60b430444c59e53016da2c72b3d1` | The assertion-inventory adaptations: per-resource-type sync counts; count parity against the live API, not the seed list; ID stability across re-sync. | +| c1 Go source (same pin) | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The `SYNC_STATUS_DISABLED` semantics: `ConnectorStatusToAPI` derives DISABLED only from an ERROR-classified sync; `sync_disabled_reason` distinguishes the data-anomaly auto-pause (`Sync paused due to significant drop in sync data` prefix) from deliberate pauses (`system`, `system-customer-opt-out`). | From 2078480dd8466074c0891bb03c111a211c60d3ae Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 20:39:05 +0000 Subject: [PATCH 08/15] CXF-221: empty-reason fallback, scope the activation anti-pattern, rollback fail-closed routing - verify-connector-output/SKILL.md: an empty or unexpected sync_disabled_reason routes through diagnose-authoring-failure (covers the ops Mode-B residual state where the reason is cleared while the DISABLED status remains). - update-and-rollback/SKILL.md: scope 'Do not redeem the approval token' to activation ('Do not redeem the activation approval token') so it no longer reads as contradicting the rollback section's instructed token redemption; add fail-closed routing for rollback precondition errors. - skills_bundle.test.ts: update the update-and-rollback literal to the scoped wording (other skills' literals unchanged). Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 2 +- skills/update-and-rollback/SKILL.md | 5 ++++- skills/verify-connector-output/SKILL.md | 6 ++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 133af23..f951067 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -153,7 +153,7 @@ const SKILL_LITERALS: Record = { "approval_token_id", "activation_epoch", "image digest", - "Do not redeem the approval token", + "Do not redeem the activation approval token", "Do not clear runtime fields, call the provisioner directly, or mutate the", "evidence is unsatisfied", "if no ACTIVE row after ~10 polls, STOP and report", diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index 0bdf02f..de96f90 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -70,6 +70,9 @@ revision under a strictly greater activation epoch. The rolled-back-from revision's serve state is untouched - the pointer move alone stops it serving. +If the rollback call fails closed (e.g. a precondition error), read the +error text and route through diagnose-authoring-failure. + ## Exit criteria - Same-catalog rerun: new revision ACTIVE with a recorded `activation_epoch`, @@ -86,7 +89,7 @@ serving. ## Anti-patterns -- Do not redeem the approval token - activation is a human OWNER step. +- Do not redeem the activation approval token - activation is a human OWNER step. - Do not reuse the managed runtime instance when the image digest does not match the target revision's pinned runtime image digest. - Do not clear runtime fields, call the provisioner directly, or mutate the diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index e9d10e1..8e19e8d 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -23,8 +23,10 @@ the exact tenant MCP titles. A data-anomaly auto-pause (reason starting `Sync paused due to significant drop in sync data`) means the sync was paused after repeated data drops - investigate the counts below and the drop reason; a - deliberate pause (customer opt-out or ops) is normal. Any other - unexpected status value routes through diagnose-authoring-failure. + deliberate pause (customer opt-out or ops) is normal; an empty or + unexpected `sync_disabled_reason` routes through + diagnose-authoring-failure. Any other unexpected status value routes + through diagnose-authoring-failure. 2. Counts: inspect the resource, entitlement, and grant counts for the intended scope. Assert count parity against the live tenant API, not against a seed list - the live product may own extra rows (default From d5024a10a591a6a6bd3c45fc836905d7f613f809 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 20:52:24 +0000 Subject: [PATCH 09/15] CXF-221: preserve the locked literal, scope via prose; blocker-protocol rollback guidance - update-and-rollback/SKILL.md: scope the no-redeem instruction via prose ('Do not redeem the approval token for activation') in both step 5 and the Anti-patterns bullet, preserving the plan-locked literal 'Do not redeem the approval token' verbatim; reword the rollback fail-closed guidance to the Blocker protocol instead of routing to diagnose-authoring-failure (whose frontmatter disclaims rollback coverage). - skills_bundle.test.ts: revert the update-and-rollback literal to the plan-locked value. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 2 +- skills/update-and-rollback/SKILL.md | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index f951067..133af23 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -153,7 +153,7 @@ const SKILL_LITERALS: Record = { "approval_token_id", "activation_epoch", "image digest", - "Do not redeem the activation approval token", + "Do not redeem the approval token", "Do not clear runtime fields, call the provisioner directly, or mutate the", "evidence is unsatisfied", "if no ACTIVE row after ~10 polls, STOP and report", diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index de96f90..d19770b 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -23,7 +23,7 @@ the exact tenant MCP titles. 4. Mint a new approval URL with `c1_connector_authoring_mint_approval_token` (`expires_in_seconds` 1-14400). GATE: non-empty `activation_url`. 5. HARD STOP at the human boundary: present the URL to a human tenant OWNER - and stop. Do not redeem the approval token. + and stop. Do not redeem the approval token for activation. 6. After the OWNER activates, poll `c1_connector_authoring_list_revision_summaries` until the target revision is `REVISION_STATUS_ACTIVE`; record its `activation_epoch`. @@ -71,7 +71,8 @@ revision's serve state is untouched - the pointer move alone stops it serving. If the rollback call fails closed (e.g. a precondition error), read the -error text and route through diagnose-authoring-failure. +error text and re-check the request fields; if it does not resolve after 2 +fix cycles, stop and report the exact error text (see Blocker protocol). ## Exit criteria @@ -89,7 +90,7 @@ error text and route through diagnose-authoring-failure. ## Anti-patterns -- Do not redeem the activation approval token - activation is a human OWNER step. +- Do not redeem the approval token for activation - activation is a human OWNER step. - Do not reuse the managed runtime instance when the image digest does not match the target revision's pinned runtime image digest. - Do not clear runtime fields, call the provisioner directly, or mutate the From 1e09561a0b7fec02c5dbb238d316adc95f6cee29 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 21:01:15 +0000 Subject: [PATCH 10/15] CXF-221: bound the force-sync verification poll in update-and-rollback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update-and-rollback/SKILL.md step 7: poll c1_connector_service_get with backoff (every 5-10s) until SYNC_STATUS_DONE; if no DONE after ~10 polls, STOP and report — mirrors the bounded-poll convention used by every other async-wait check in the bundle (ForceSync is async, minutes-to-hours). - skills_bundle.test.ts: lock the new bound literal. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 1 + skills/update-and-rollback/SKILL.md | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 133af23..8eb5015 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -157,6 +157,7 @@ const SKILL_LITERALS: Record = { "Do not clear runtime fields, call the provisioner directly, or mutate the", "evidence is unsatisfied", "if no ACTIVE row after ~10 polls, STOP and report", + "if no DONE after ~10 polls, STOP and report", "Do not print, log, or otherwise expose the OWNER bearer token value", ], "diagnose-authoring-failure": [ diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index d19770b..5660575 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -31,8 +31,9 @@ the exact tenant MCP titles. return to the draft-test step and confirm a fresh PASS row binds this revision before minting a new approval URL. Poll with backoff (e.g. every 5-10s); if no ACTIVE row after ~10 polls, STOP and report. -7. Call `c1_connector_service_force_sync`; verify via - `c1_connector_service_get` that `status.status` is `SYNC_STATUS_DONE`. +7. Call `c1_connector_service_force_sync`; poll `c1_connector_service_get` + with backoff (e.g. every 5-10s) until `status.status` is + `SYNC_STATUS_DONE`; if no DONE after ~10 polls, STOP and report. ## Rotation STOP (known limitation) From 74529132e3d43f4322df7c3caeecdfc5e0a15fac Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 21:15:10 +0000 Subject: [PATCH 11/15] CXF-221: terminal-state branches in the force-sync verification poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update-and-rollback/SKILL.md step 7: after the bounded DONE poll, an SYNC_STATUS_ERROR row routes through diagnose-authoring-failure with status.lastError; an SYNC_STATUS_DISABLED row routes through diagnose-authoring-failure unless sync_disabled_reason indicates a deliberate pause (customer opt-out or ops) — mirrors verify-connector-output's taxonomy so an immediate sync failure is not masked by the poll budget. - skills_bundle.test.ts: lock SYNC_STATUS_ERROR in update-and-rollback. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 1 + skills/update-and-rollback/SKILL.md | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 8eb5015..af94aec 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -158,6 +158,7 @@ const SKILL_LITERALS: Record = { "evidence is unsatisfied", "if no ACTIVE row after ~10 polls, STOP and report", "if no DONE after ~10 polls, STOP and report", + "SYNC_STATUS_ERROR", "Do not print, log, or otherwise expose the OWNER bearer token value", ], "diagnose-authoring-failure": [ diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index 5660575..5a59e39 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -33,7 +33,12 @@ the exact tenant MCP titles. 5-10s); if no ACTIVE row after ~10 polls, STOP and report. 7. Call `c1_connector_service_force_sync`; poll `c1_connector_service_get` with backoff (e.g. every 5-10s) until `status.status` is - `SYNC_STATUS_DONE`; if no DONE after ~10 polls, STOP and report. + `SYNC_STATUS_DONE`; if no DONE after ~10 polls, STOP and report. If the + status row reports `SYNC_STATUS_ERROR`, read `status.lastError` and route + through diagnose-authoring-failure; if `SYNC_STATUS_DISABLED`, read the + connector's `sync_disabled_reason` and route through + diagnose-authoring-failure unless the reason indicates a deliberate + pause (customer opt-out or ops). ## Rotation STOP (known limitation) From e8a793c99f51a3d2b7ca48890b9471ec2a647398 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 21:25:59 +0000 Subject: [PATCH 12/15] CXF-221: terminal-status poll exit, DISABLED literal, diagnose frontmatter carve-out - update-and-rollback/SKILL.md step 7: the poll now exits on any terminal status (DONE, ERROR, or DISABLED) so an immediate sync failure does not burn the ~10-poll budget; bound reworded to 'no terminal status after ~10 polls, STOP and report'. - update-and-rollback/SOURCES.md: provenance row for the ERROR/DISABLED terminal-state semantics from the c1 Go source at the same pin. - diagnose-authoring-failure/SKILL.md: narrow the rollback anti-trigger to 'updating or rolling back a healthy live connector' so a failed-sync route from update-and-rollback step 7 does not collide with the disclaimer. - skills_bundle.test.ts: lock 'no terminal status after ~10 polls, STOP and report' and SYNC_STATUS_DISABLED in update-and-rollback. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 3 ++- skills/diagnose-authoring-failure/SKILL.md | 2 +- skills/update-and-rollback/SKILL.md | 7 ++++--- skills/update-and-rollback/SOURCES.md | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index af94aec..43d5b9c 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -157,8 +157,9 @@ const SKILL_LITERALS: Record = { "Do not clear runtime fields, call the provisioner directly, or mutate the", "evidence is unsatisfied", "if no ACTIVE row after ~10 polls, STOP and report", - "if no DONE after ~10 polls, STOP and report", + "no terminal status after ~10 polls, STOP and report", "SYNC_STATUS_ERROR", + "SYNC_STATUS_DISABLED", "Do not print, log, or otherwise expose the OWNER bearer token value", ], "diagnose-authoring-failure": [ diff --git a/skills/diagnose-authoring-failure/SKILL.md b/skills/diagnose-authoring-failure/SKILL.md index 50a3d45..2327b4a 100644 --- a/skills/diagnose-authoring-failure/SKILL.md +++ b/skills/diagnose-authoring-failure/SKILL.md @@ -1,6 +1,6 @@ --- name: diagnose-authoring-failure -description: Use when a build, draft test, activation, or production sync fails and you need the symptom-to-cause-to-fix route. Do not use when the connector is healthy and you are verifying sync output - use verify-connector-output; do not use when updating or rolling back a live connector - use update-and-rollback. +description: Use when a build, draft test, activation, or production sync fails and you need the symptom-to-cause-to-fix route. Do not use when the connector is healthy and you are verifying sync output - use verify-connector-output; do not use when updating or rolling back a healthy live connector - use update-and-rollback. version: 0.1.0 --- diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index 5a59e39..003b983 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -33,9 +33,10 @@ the exact tenant MCP titles. 5-10s); if no ACTIVE row after ~10 polls, STOP and report. 7. Call `c1_connector_service_force_sync`; poll `c1_connector_service_get` with backoff (e.g. every 5-10s) until `status.status` is - `SYNC_STATUS_DONE`; if no DONE after ~10 polls, STOP and report. If the - status row reports `SYNC_STATUS_ERROR`, read `status.lastError` and route - through diagnose-authoring-failure; if `SYNC_STATUS_DISABLED`, read the + `SYNC_STATUS_DONE`, `SYNC_STATUS_ERROR`, or `SYNC_STATUS_DISABLED`; if + no terminal status after ~10 polls, STOP and report. If the status row + reports `SYNC_STATUS_ERROR`, read `status.lastError` and route through + diagnose-authoring-failure; if `SYNC_STATUS_DISABLED`, read the connector's `sync_disabled_reason` and route through diagnose-authoring-failure unless the reason indicates a deliberate pause (customer opt-out or ops). diff --git a/skills/update-and-rollback/SOURCES.md b/skills/update-and-rollback/SOURCES.md index 799d892..24a4c42 100644 --- a/skills/update-and-rollback/SOURCES.md +++ b/skills/update-and-rollback/SOURCES.md @@ -9,3 +9,4 @@ model memory). Source-of-truth precedence: (a) MCP-served guide, (b) | MCP-served guide | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The "Updating an active connector" contract: reuse the existing managed runtime instance, update the draft source, build a new revision, fresh PASS evidence, mint a new approval URL, human OWNER activates, poll `list_revision_summaries` until ACTIVE, record `activation_epoch`, force sync. | | Authoring proto | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | `MintApprovalToken` (`expires_in_seconds` 1-14400, `activation_url`); `ListRevisionSummaries` + `RevisionStatus` enum (`REVISION_STATUS_ACTIVE` = 1, `activation_epoch` on the ACTIVE row). The proto exposes no rollback RPC - its service comment notes rollback is the REST-only endpoint; the rollback contract lives in the lifecycle doc row below. | | Lifecycle doc | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The "Updating and rolling back a live connector" section: the image-digest reuse rule; the rotation STOP verbatim (`serve image does not match the revision-pinned runtime image`; do not clear runtime fields, call the provisioner directly, or mutate the deployment or AWS resources; record tenant/catalog/app/connector/target-revision IDs; escalate to Connector Authoring / managed-runtime engineering); the REST-only OWNER-gated rollback body and the strictly-greater-activation-epoch pointer move. | +| c1 Go source (same pin) | c1 `2e5f53eb441a93087d9754085ca17a5061e125ea` | The `SYNC_STATUS_ERROR` / `SYNC_STATUS_DISABLED` terminal-state semantics: `ConnectorStatusToAPI` derives DISABLED only from an ERROR-classified sync; `sync_disabled_reason` distinguishes the data-anomaly auto-pause from deliberate pauses. | From 79117b7a440010a6eb8d17ec76b4247b55679cc4 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 21:36:21 +0000 Subject: [PATCH 13/15] CXF-221: include DISABLED in the verify RUNNING-poll exit condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - verify-connector-output/SKILL.md step 1: the RUNNING poll now exits on DONE, ERROR, or DISABLED (a mid-poll data-anomaly auto-pause no longer burns the ~10-poll budget before the DISABLED branch applies) — mirrors update-and-rollback step 7's terminal-state race. - skills_bundle.test.ts: lock 'terminal status after ~10 polls, STOP and report' for verify-connector-output. Co-authored-by: c1-squire-dev[bot] --- evals/runner/skills_bundle.test.ts | 2 +- skills/verify-connector-output/SKILL.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/evals/runner/skills_bundle.test.ts b/evals/runner/skills_bundle.test.ts index 43d5b9c..02a3093 100644 --- a/evals/runner/skills_bundle.test.ts +++ b/evals/runner/skills_bundle.test.ts @@ -137,7 +137,7 @@ const SKILL_LITERALS: Record = { "never invent data to make a demo appear complete", "SYNC_STATUS_DONE", "SYNC_STATUS_RUNNING", - "DONE/ERROR after ~10 polls, STOP and report", + "terminal status after ~10 polls, STOP and report", "data-anomaly auto-pause", "significant drop in sync data", "sync_disabled", diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index 8e19e8d..47a1045 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -17,8 +17,9 @@ the exact tenant MCP titles. normal for authored connectors). GATE: DONE. STOP if the status row reports an error - read `status.lastError` and route through diagnose-authoring-failure. If `status.status` is `SYNC_STATUS_RUNNING`, - poll with backoff (e.g. every 5-10s) until DONE or ERROR; if no - DONE/ERROR after ~10 polls, STOP and report. `SYNC_STATUS_DISABLED` is + poll with backoff (e.g. every 5-10s) until DONE, ERROR, or DISABLED; if + no terminal status after ~10 polls, STOP and report. + `SYNC_STATUS_DISABLED` is an error-classified outcome: read the connector's `sync_disabled_reason`. A data-anomaly auto-pause (reason starting `Sync paused due to significant drop in sync data`) means the sync was paused after repeated From 14d811d50456decf1ea7b78e19c30120597c6c47 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 22:22:40 +0000 Subject: [PATCH 14/15] =?UTF-8?q?CXF-221:=20correct=20the=20skills=20READM?= =?UTF-8?q?E=20intro=20=E2=80=94=20five=20funnel=20+=20two=20pre-1=20skill?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The seven prior skills are five funnel skills and two pre-1 skills (design-access-model, source-openapi-spec); the intro now says so. Co-authored-by: c1-squire-dev[bot] --- skills/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/skills/README.md b/skills/README.md index 5d7f750..aca394a 100644 --- a/skills/README.md +++ b/skills/README.md @@ -1,11 +1,12 @@ # Agent skills -The ten skills shipped in this batch: the seven funnel skills authored -against the v0.0.26 DSL contract and the 23-tool tenant MCP surface, plus -three post-funnel and cross-cutting skills (`verify-connector-output`, -`update-and-rollback`, `diagnose-authoring-failure`) over the tenant -connector and authoring tool surface. Each skill's `SOURCES.md` names the -pinned sources with their SHAs. +The ten skills shipped in this batch: the five funnel skills and two +pre-1 skills authored against the v0.0.26 DSL contract and the 23-tool +tenant MCP surface, plus three post-funnel and cross-cutting skills +(`verify-connector-output`, `update-and-rollback`, +`diagnose-authoring-failure`) over the tenant connector and authoring +tool surface. Each skill's `SOURCES.md` names the pinned sources with +their SHAs. | Skill | Stage coverage | Source basis | |---|---|---| From 1e3c69532afee923a3955edcee8f622db20ad448 Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Fri, 4 Sep 2026 22:25:15 +0000 Subject: [PATCH 15/15] CXF-221: address bot review suggestions on the three new skills - update-and-rollback: state the rollback actor (agent) and OWNER token provenance (env/secret store, never pasted into chat) - verify-connector-output: name c1_connector_service_force_sync and the poll-to-terminal-status bound in the ID-stability step - diagnose-authoring-failure: clarify that the baton-admin skill's repo-local CLI tooling is replaced by the tenant MCP tools - evals/skills-bundle/README.md: note the three new skills' c1 pin Co-authored-by: c1-squire-dev[bot] --- evals/skills-bundle/README.md | 4 +++- skills/diagnose-authoring-failure/SKILL.md | 3 ++- skills/update-and-rollback/SKILL.md | 4 +++- skills/verify-connector-output/SKILL.md | 9 ++++++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/evals/skills-bundle/README.md b/evals/skills-bundle/README.md index d701b25..83a9f1d 100644 --- a/evals/skills-bundle/README.md +++ b/evals/skills-bundle/README.md @@ -16,7 +16,9 @@ and cross-cutting skills at `0.1.0`: router over the common-failures table, the draft-test FAIL reading, and where logs live. -The seven prior skills are unchanged; the bundle version is `0.4.0`. +The seven prior skills are unchanged; the bundle version is `0.4.0`. The +three new skills pin the c1 contract sources at `2e5f53eb…` (see each +skill's SOURCES.md). ## v0.3.0 — the seven skills diff --git a/skills/diagnose-authoring-failure/SKILL.md b/skills/diagnose-authoring-failure/SKILL.md index 2327b4a..5f44bee 100644 --- a/skills/diagnose-authoring-failure/SKILL.md +++ b/skills/diagnose-authoring-failure/SKILL.md @@ -9,7 +9,8 @@ version: 0.1.0 Symptom -> cause -> fix router for authored-connector failures. Built on the lifecycle doc's common-failures table, the draft-test evidence reading, and where logs live. Ports the taxonomy approach of baton-admin -`diagnose-connector-failure`; replaces all repo tooling. +`diagnose-connector-failure`; the baton-admin skill's repo-local CLI +tooling is replaced by the tenant MCP tools named below. ## Workflow diff --git a/skills/update-and-rollback/SKILL.md b/skills/update-and-rollback/SKILL.md index 003b983..8d713cc 100644 --- a/skills/update-and-rollback/SKILL.md +++ b/skills/update-and-rollback/SKILL.md @@ -58,7 +58,9 @@ managed-runtime engineering. To roll back to a previously activated revision, mint an approval token for the rollback target revision and redeem it against the rollback endpoint - -unlike activation, rollback is REST-only and OWNER-gated. Use the product +unlike activation, rollback is REST-only and OWNER-gated. The agent executes +the POST with an OWNER bearer token read from the environment or secret +store - never ask a human to paste the token into chat. Use the product base URL and OWNER bearer token: ``` diff --git a/skills/verify-connector-output/SKILL.md b/skills/verify-connector-output/SKILL.md index 47a1045..2770d52 100644 --- a/skills/verify-connector-output/SKILL.md +++ b/skills/verify-connector-output/SKILL.md @@ -35,9 +35,12 @@ the exact tenant MCP titles. 3. Grant wiring: verify that every grant principal references an emitted resource and every grant entitlement ID references an emitted entitlement. GATE: no dangling principal or entitlement ID. -4. ID stability: re-sync and confirm the emitted resource and entitlement - IDs are stable across the re-sync - no churn in identity fields. - GATE: IDs unchanged. +4. ID stability: call `c1_connector_service_force_sync`, then poll + `c1_connector_service_get` with backoff (e.g. every 5-10s) until + `status.status` is `SYNC_STATUS_DONE`, `SYNC_STATUS_ERROR`, or + `SYNC_STATUS_DISABLED`; if no terminal status after ~10 polls, STOP and + report. Confirm the emitted resource and entitlement IDs are stable + across the re-sync - no churn in identity fields. GATE: IDs unchanged. 5. UI spot-check: open `/admin/connector///` on the product base URL and confirm the connector's resources and grants appear. GATE: resources and grants visible.