SKILL.md v3 compliance

Fuels Electricity Commit

Commit approved fuels-and-electricity bills so GHG emissions processing actually starts.

No reviews yet 0 installs
Takes bills the user has approved from bills.json (produced by fuels-electricity-extract and triaged by the orchestrator) and commits them via qb.commit_ingest. Presents any still-parked rows grouped by reason for a single triage decision rather than one at a time, naming the source values and candidate suggestions from each issue. Strips reference and derived from each bill before it reaches properties, and validates every bill's required fields and enum values locally before any network call — the fuels-and-electricity-bills resource type sets additionalProperties: false, so committing a bill unmodified or with a bad enum value is rejected outright. atomic and projectToCortex are always set so a partial failure never leaves half a client's bills committed. A failed commit — local or server-side — is a hard stop and is always surfaced, never reported as success; a Cortex projection failure is surfaced as a distinct warning rather than silence. Never quotes an emissions figure in chat — commit_ingest returns once records are written and processing has started, not once emissions are calculated.
emissionsfuelselectricityghgcommitingestcompliance

Versions

  • v3 # Changelog ## v2 - **Local validation now delegates to the shared `resource_import` module** rather than duplicating required-field and enum checking. The behaviour for this skill's flat schema is unchanged — the existing suite is the regression gate — but the shared module also descends into nested `required` lists, which this schema does not use and the supplier schema does. - **A bill may now carry `tags`.** Labels are promoted to the RECORD level (alongside `reference`), never left in `properties` where `additionalProperties: false` would reject them. The server resolves each label against the org's tag definitions and creates it when missing. A tag failure is reported as a warning, not a hard stop — the bill itself is still written, and `summarise_report` names the failed reference in the same words `resource-import` uses, so the two skills read consistently. - `lib/resource_import.py` is a byte-identical copy of the module in the `resource-import` skill. Skill bundles are copied into the sandbox independently and there is no cross-skill import path, so the duplication is deliberate; `apps/community/curator/src/app/resourceImportModuleCopies.spec.ts` compares the two files byte-for-byte, so the claim is enforced by a suite rather than by having been checked once by hand. #### Behavioural divergences from the validator this port replaced Surfacing these is the stated purpose of the port. Every one is harmless for this skill's flat schema — but none is strictly a no-op, so all are listed rather than only the ones that bite. Compared against the deleted inline `_validate_record`. 1. **Nested `required` lists are descended into** (noted above). This schema has none; the supplier schema does. 2. **Enum checking descends into object sub-schemas** too, where the old `_enum_by_property` read top-level `properties` only. Again, no nested object here. 3. **`_is_blank` treats a whitespace-only string as missing**, where the old inline check (`properties.get(field) in (None, "")`) did not. A bill whose required field is `" "` is now reported `local_validation_failed` rather than committed. Arguably the better answer, but it is a change. 4. **`_missing_required` treats an empty dict `{}` as missing**, where `properties.get(field) in (None, "")` did not. Reachable only for an object-valued required field: this flat schema has none, the supplier schema (`address`) does. A fifth difference exists but is unreachable here: `validate_record` also rejects a record with a falsy `reference` or `name`, which the old inline check never looked at. `_reference_for` always returns at least `"|||"` and `_bill_name` always returns a non-empty string, so no bill built by this module can trigger it. ## v1 - Initial release: `lib/commit.py` builds the `qb.commit_ingest` payload for bills the user has approved in `/workspace/work/outputs/bills.json`, validates every record locally, calls the broker, and reports the result. - **`properties` is NOT the bill itself** (correction to this task's own brief, which described it that way — committing a bill unmodified would be REJECTED). The `fuels-and-electricity-bills` resource type sets `additionalProperties: false`, and an extracted bill carries two keys outside that schema: `reference` (record-level, used for idempotent upsert) and `derived` (triage-only, marks which fields were computed rather than read from source). `build_commit_payload` strips both, and anything else the schema doesn't recognise, via a **whitelist read live from the resource type's own `propertiesSchemaExtension.properties` keys** — never a hand-maintained duplicate list that could drift out of sync with the schema it must satisfy. - **`reference` is always recomputed, never trusted verbatim.** A triaged bill's `reference` (`siteId|type|billingDate|billingIdentifier`, matching the extract skill's own formula) is computed fresh from the bill's own fields on every call — a hand-typed or stale `reference` left over from triage can never silently break the idempotent upsert with the wrong key, and a bill missing `reference` entirely no longer raises a bare `KeyError`. - **Every record is validated locally — required fields and enum values — before any network call.** `validate_records` checks each record against the SAME schema `build_commit_payload` reads (again, no duplicated list): a triaged row still missing a required field, or still carrying an unrecognised `type`/`unit` value (e.g. `"light oil / diesel"` instead of the closed enum's `"Diesel"`), stops the WHOLE atomic batch immediately with a specific per-bill reason — never reaches the broker as an opaque 502 that would otherwise fail every other, genuinely valid bill in the same commit alongside it. - **The broker call uses the real SDK signature.** An earlier version of this skill called `qb.commit_ingest(**payload)` with the HTTP route's camelCase shape (`orgId`, `workspaceId`, `projectToCortex`, ...) — this does not match the actual Python SDK (`cortex-sandbox`'s `qonstrue_broker.commit_ingest(resource_types, relationship_types, records, *, atomic=None, project_to_cortex=None)`, which takes snake_case keywords and NO `orgId`/`workspaceId` at all — "Org and workspace identity are injected by the broker" per the SDK's own docstring) and would have raised `TypeError` on every live run. `_call_commit_ingest` now maps onto the real signature at the one call site that actually touches the broker; `build_commit_payload`'s own return shape (still camelCase, still carrying `orgId`/`workspaceId` for backward compatibility with its existing tests) is unchanged. - **A hard failure from the broker (a rejected atomic batch, a timeout, an unreachable sidecar) is caught and folded into the same report shape** `summarise_report`/`commit_succeeded` already know how to render — never a raw traceback reaching the user. Since `atomic: true` means the server rolls back the WHOLE transaction on any bad row, every record in the attempt is reported failed, not just whichever one triggered the exception. - **The resource type definition is only sent to the broker when it isn't already installed.** `commit_ingest`'s own resource-type upsert REPLACES whatever schema it's given rather than merging it, but the org's live schema may have been additively merged since blueprint install (`installBlueprintResourceTypesToOrg` uses `mergeProperties: true`) — resending our locally-bundled copy on every call would silently clobber any such addition. `_call_commit_ingest` checks `list_resource_types()` first and sends `resource_types: []` once the type is confirmed present — the same pattern the sanusi `batch-process` skill uses. - `atomic: true` and `projectToCortex: true` are always set — a partial failure must never leave half a client's bills committed, and a successful commit must be immediately visible under `/workspace/local/fuels-and-electricity-bills`. - `commit_succeeded(report)` is `False` whenever `report.records.failed` is non-empty (locally caught or server-reported). A failed commit is a hard stop: every failed `reference`/`error`/`detail` is surfaced via `summarise_report`, never silently dropped or reported as a success. When nothing was committed at all (a local validation stop, or a full atomic rollback), the summary says so plainly rather than claiming records were written. - **Cortex projection failures are surfaced as a distinct warning, not silence.** `report.cortex.failed` is non-empty when a bill was genuinely committed (the resource record was written, `records.failed` is empty) but failed to project into the Cortex view — `summarise_report` now includes an explicit warning for this case rather than only the (accurate but incomplete) "committed" headline, so a user is never told a bill is visible under `/workspace/local/fuels-and-electricity-bills` when it isn't yet. This does not, by itself, flip `commit_succeeded` to `False` — the underlying record write did succeed. - `summarise_report(report)` never quotes an emissions figure — `qb.commit_ingest` returns once records are written and `Integrations.Process.SiteEmissions` has been published, not once emissions are actually calculated. - `SKILL.md` documents the triage gate this skill's bundle assumes has already happened: parked rows from `bills.json`'s `issues` array grouped by _reason_ (not by row), naming the real issue fields (`parked_row`, `sourceSheet`/`sourceCell`, `typeCandidates`/`unitCandidates`) so the agent can quote actual source values and suggestions to the user, with sheet-level issues (`"row": null`) grouped as their own reason rather than folded into "missing everything." It also states the bill shape a triaged row must have (the resource type's required/optional fields) and makes clear `reference` never needs to be hand-computed. ### Known limitations / assumptions flagged for review - Locating this blueprint's bundled `resource-types/`/`relationship-types/` folders at runtime (the fallback path, used only when `list_resource_types()` can't confirm the type is already installed) globs specifically for `resource-types/fuels-and-electricity-bills.json` and verifies the loaded JSON's own `slug`, raising loudly if more than one installed blueprint's bundle matches — but this sandbox-glob branch is necessarily unexercised by any test in this repo checkout (`/workspace` doesn't exist here). The local dev/test fallback (the human slug `fuels-and-electricity-emissions-service`, a real sibling directory in this checkout) is what every test actually runs against. - Two triaged rows that end up sharing an identical recomputed `reference` (no `billingIdentifier` to distinguish them) are not detected by this skill — the extract skill already prevents this among its own output (`_split_duplicate_references`), but a triage decision could theoretically reintroduce a collision (e.g. two different parked rows both corrected to the same site/type/date with no identifier). Not implemented here; flagged as a residual gap rather than silently accepted. 8/28/2026
  • v2 # Changelog ## v2 - **Local validation now delegates to the shared `resource_import` module** rather than duplicating required-field and enum checking. The behaviour for this skill's flat schema is unchanged — the existing suite is the regression gate — but the shared module also descends into nested `required` lists, which this schema does not use and the supplier schema does. - **A bill may now carry `tags`.** Labels are promoted to the RECORD level (alongside `reference`), never left in `properties` where `additionalProperties: false` would reject them. The server resolves each label against the org's tag definitions and creates it when missing. A tag failure is reported as a warning, not a hard stop — the bill itself is still written, and `summarise_report` names the failed reference in the same words `resource-import` uses, so the two skills read consistently. - `lib/resource_import.py` is a byte-identical copy of the module in the `resource-import` skill. Skill bundles are copied into the sandbox independently and there is no cross-skill import path, so the duplication is deliberate; `apps/community/curator/src/app/resourceImportModuleCopies.spec.ts` compares the two files byte-for-byte, so the claim is enforced by a suite rather than by having been checked once by hand. #### Behavioural divergences from the validator this port replaced Surfacing these is the stated purpose of the port. Every one is harmless for this skill's flat schema — but none is strictly a no-op, so all are listed rather than only the ones that bite. Compared against the deleted inline `_validate_record`. 1. **Nested `required` lists are descended into** (noted above). This schema has none; the supplier schema does. 2. **Enum checking descends into object sub-schemas** too, where the old `_enum_by_property` read top-level `properties` only. Again, no nested object here. 3. **`_is_blank` treats a whitespace-only string as missing**, where the old inline check (`properties.get(field) in (None, "")`) did not. A bill whose required field is `" "` is now reported `local_validation_failed` rather than committed. Arguably the better answer, but it is a change. 4. **`_missing_required` treats an empty dict `{}` as missing**, where `properties.get(field) in (None, "")` did not. Reachable only for an object-valued required field: this flat schema has none, the supplier schema (`address`) does. A fifth difference exists but is unreachable here: `validate_record` also rejects a record with a falsy `reference` or `name`, which the old inline check never looked at. `_reference_for` always returns at least `"|||"` and `_bill_name` always returns a non-empty string, so no bill built by this module can trigger it. ## v1 - Initial release: `lib/commit.py` builds the `qb.commit_ingest` payload for bills the user has approved in `/workspace/work/outputs/bills.json`, validates every record locally, calls the broker, and reports the result. - **`properties` is NOT the bill itself** (correction to this task's own brief, which described it that way — committing a bill unmodified would be REJECTED). The `fuels-and-electricity-bills` resource type sets `additionalProperties: false`, and an extracted bill carries two keys outside that schema: `reference` (record-level, used for idempotent upsert) and `derived` (triage-only, marks which fields were computed rather than read from source). `build_commit_payload` strips both, and anything else the schema doesn't recognise, via a **whitelist read live from the resource type's own `propertiesSchemaExtension.properties` keys** — never a hand-maintained duplicate list that could drift out of sync with the schema it must satisfy. - **`reference` is always recomputed, never trusted verbatim.** A triaged bill's `reference` (`siteId|type|billingDate|billingIdentifier`, matching the extract skill's own formula) is computed fresh from the bill's own fields on every call — a hand-typed or stale `reference` left over from triage can never silently break the idempotent upsert with the wrong key, and a bill missing `reference` entirely no longer raises a bare `KeyError`. - **Every record is validated locally — required fields and enum values — before any network call.** `validate_records` checks each record against the SAME schema `build_commit_payload` reads (again, no duplicated list): a triaged row still missing a required field, or still carrying an unrecognised `type`/`unit` value (e.g. `"light oil / diesel"` instead of the closed enum's `"Diesel"`), stops the WHOLE atomic batch immediately with a specific per-bill reason — never reaches the broker as an opaque 502 that would otherwise fail every other, genuinely valid bill in the same commit alongside it. - **The broker call uses the real SDK signature.** An earlier version of this skill called `qb.commit_ingest(**payload)` with the HTTP route's camelCase shape (`orgId`, `workspaceId`, `projectToCortex`, ...) — this does not match the actual Python SDK (`cortex-sandbox`'s `qonstrue_broker.commit_ingest(resource_types, relationship_types, records, *, atomic=None, project_to_cortex=None)`, which takes snake_case keywords and NO `orgId`/`workspaceId` at all — "Org and workspace identity are injected by the broker" per the SDK's own docstring) and would have raised `TypeError` on every live run. `_call_commit_ingest` now maps onto the real signature at the one call site that actually touches the broker; `build_commit_payload`'s own return shape (still camelCase, still carrying `orgId`/`workspaceId` for backward compatibility with its existing tests) is unchanged. - **A hard failure from the broker (a rejected atomic batch, a timeout, an unreachable sidecar) is caught and folded into the same report shape** `summarise_report`/`commit_succeeded` already know how to render — never a raw traceback reaching the user. Since `atomic: true` means the server rolls back the WHOLE transaction on any bad row, every record in the attempt is reported failed, not just whichever one triggered the exception. - **The resource type definition is only sent to the broker when it isn't already installed.** `commit_ingest`'s own resource-type upsert REPLACES whatever schema it's given rather than merging it, but the org's live schema may have been additively merged since blueprint install (`installBlueprintResourceTypesToOrg` uses `mergeProperties: true`) — resending our locally-bundled copy on every call would silently clobber any such addition. `_call_commit_ingest` checks `list_resource_types()` first and sends `resource_types: []` once the type is confirmed present — the same pattern the sanusi `batch-process` skill uses. - `atomic: true` and `projectToCortex: true` are always set — a partial failure must never leave half a client's bills committed, and a successful commit must be immediately visible under `/workspace/local/fuels-and-electricity-bills`. - `commit_succeeded(report)` is `False` whenever `report.records.failed` is non-empty (locally caught or server-reported). A failed commit is a hard stop: every failed `reference`/`error`/`detail` is surfaced via `summarise_report`, never silently dropped or reported as a success. When nothing was committed at all (a local validation stop, or a full atomic rollback), the summary says so plainly rather than claiming records were written. - **Cortex projection failures are surfaced as a distinct warning, not silence.** `report.cortex.failed` is non-empty when a bill was genuinely committed (the resource record was written, `records.failed` is empty) but failed to project into the Cortex view — `summarise_report` now includes an explicit warning for this case rather than only the (accurate but incomplete) "committed" headline, so a user is never told a bill is visible under `/workspace/local/fuels-and-electricity-bills` when it isn't yet. This does not, by itself, flip `commit_succeeded` to `False` — the underlying record write did succeed. - `summarise_report(report)` never quotes an emissions figure — `qb.commit_ingest` returns once records are written and `Integrations.Process.SiteEmissions` has been published, not once emissions are actually calculated. - `SKILL.md` documents the triage gate this skill's bundle assumes has already happened: parked rows from `bills.json`'s `issues` array grouped by _reason_ (not by row), naming the real issue fields (`parked_row`, `sourceSheet`/`sourceCell`, `typeCandidates`/`unitCandidates`) so the agent can quote actual source values and suggestions to the user, with sheet-level issues (`"row": null`) grouped as their own reason rather than folded into "missing everything." It also states the bill shape a triaged row must have (the resource type's required/optional fields) and makes clear `reference` never needs to be hand-computed. ### Known limitations / assumptions flagged for review - Locating this blueprint's bundled `resource-types/`/`relationship-types/` folders at runtime (the fallback path, used only when `list_resource_types()` can't confirm the type is already installed) globs specifically for `resource-types/fuels-and-electricity-bills.json` and verifies the loaded JSON's own `slug`, raising loudly if more than one installed blueprint's bundle matches — but this sandbox-glob branch is necessarily unexercised by any test in this repo checkout (`/workspace` doesn't exist here). The local dev/test fallback (the human slug `fuels-and-electricity-emissions-service`, a real sibling directory in this checkout) is what every test actually runs against. - Two triaged rows that end up sharing an identical recomputed `reference` (no `billingIdentifier` to distinguish them) are not detected by this skill — the extract skill already prevents this among its own output (`_split_duplicate_references`), but a triage decision could theoretically reintroduce a collision (e.g. two different parked rows both corrected to the same site/type/date with no identifier). Not implemented here; flagged as a residual gap rather than silently accepted. 8/27/2026
  • v1 # Changelog ## v1 - Initial release: `lib/commit.py` builds the `qb.commit_ingest` payload for bills the user has approved in `/workspace/work/outputs/bills.json`, validates every record locally, calls the broker, and reports the result. - **`properties` is NOT the bill itself** (correction to this task's own brief, which described it that way — committing a bill unmodified would be REJECTED). The `fuels-and-electricity-bills` resource type sets `additionalProperties: false`, and an extracted bill carries two keys outside that schema: `reference` (record-level, used for idempotent upsert) and `derived` (triage-only, marks which fields were computed rather than read from source). `build_commit_payload` strips both, and anything else the schema doesn't recognise, via a **whitelist read live from the resource type's own `propertiesSchemaExtension.properties` keys** — never a hand-maintained duplicate list that could drift out of sync with the schema it must satisfy. - **`reference` is always recomputed, never trusted verbatim.** A triaged bill's `reference` (`siteId|type|billingDate|billingIdentifier`, matching the extract skill's own formula) is computed fresh from the bill's own fields on every call — a hand-typed or stale `reference` left over from triage can never silently break the idempotent upsert with the wrong key, and a bill missing `reference` entirely no longer raises a bare `KeyError`. - **Every record is validated locally — required fields and enum values — before any network call.** `validate_records` checks each record against the SAME schema `build_commit_payload` reads (again, no duplicated list): a triaged row still missing a required field, or still carrying an unrecognised `type`/`unit` value (e.g. `"light oil / diesel"` instead of the closed enum's `"Diesel"`), stops the WHOLE atomic batch immediately with a specific per-bill reason — never reaches the broker as an opaque 502 that would otherwise fail every other, genuinely valid bill in the same commit alongside it. - **The broker call uses the real SDK signature.** An earlier version of this skill called `qb.commit_ingest(**payload)` with the HTTP route's camelCase shape (`orgId`, `workspaceId`, `projectToCortex`, ...) — this does not match the actual Python SDK (`cortex-sandbox`'s `qonstrue_broker.commit_ingest(resource_types, relationship_types, records, *, atomic=None, project_to_cortex=None)`, which takes snake_case keywords and NO `orgId`/`workspaceId` at all — "Org and workspace identity are injected by the broker" per the SDK's own docstring) and would have raised `TypeError` on every live run. `_call_commit_ingest` now maps onto the real signature at the one call site that actually touches the broker; `build_commit_payload`'s own return shape (still camelCase, still carrying `orgId`/`workspaceId` for backward compatibility with its existing tests) is unchanged. - **A hard failure from the broker (a rejected atomic batch, a timeout, an unreachable sidecar) is caught and folded into the same report shape** `summarise_report`/`commit_succeeded` already know how to render — never a raw traceback reaching the user. Since `atomic: true` means the server rolls back the WHOLE transaction on any bad row, every record in the attempt is reported failed, not just whichever one triggered the exception. - **The resource type definition is only sent to the broker when it isn't already installed.** `commit_ingest`'s own resource-type upsert REPLACES whatever schema it's given rather than merging it, but the org's live schema may have been additively merged since blueprint install (`installBlueprintResourceTypesToOrg` uses `mergeProperties: true`) — resending our locally-bundled copy on every call would silently clobber any such addition. `_call_commit_ingest` checks `list_resource_types()` first and sends `resource_types: []` once the type is confirmed present — the same pattern the sanusi `batch-process` skill uses. - `atomic: true` and `projectToCortex: true` are always set — a partial failure must never leave half a client's bills committed, and a successful commit must be immediately visible under `/workspace/local/fuels-and-electricity-bills`. - `commit_succeeded(report)` is `False` whenever `report.records.failed` is non-empty (locally caught or server-reported). A failed commit is a hard stop: every failed `reference`/`error`/`detail` is surfaced via `summarise_report`, never silently dropped or reported as a success. When nothing was committed at all (a local validation stop, or a full atomic rollback), the summary says so plainly rather than claiming records were written. - **Cortex projection failures are surfaced as a distinct warning, not silence.** `report.cortex.failed` is non-empty when a bill was genuinely committed (the resource record was written, `records.failed` is empty) but failed to project into the Cortex view — `summarise_report` now includes an explicit warning for this case rather than only the (accurate but incomplete) "committed" headline, so a user is never told a bill is visible under `/workspace/local/fuels-and-electricity-bills` when it isn't yet. This does not, by itself, flip `commit_succeeded` to `False` — the underlying record write did succeed. - `summarise_report(report)` never quotes an emissions figure — `qb.commit_ingest` returns once records are written and `Integrations.Process.SiteEmissions` has been published, not once emissions are actually calculated. - `SKILL.md` documents the triage gate this skill's bundle assumes has already happened: parked rows from `bills.json`'s `issues` array grouped by _reason_ (not by row), naming the real issue fields (`parked_row`, `sourceSheet`/`sourceCell`, `typeCandidates`/`unitCandidates`) so the agent can quote actual source values and suggestions to the user, with sheet-level issues (`"row": null`) grouped as their own reason rather than folded into "missing everything." It also states the bill shape a triaged row must have (the resource type's required/optional fields) and makes clear `reference` never needs to be hand-computed. ### Known limitations / assumptions flagged for review - Locating this blueprint's bundled `resource-types/`/`relationship-types/` folders at runtime (the fallback path, used only when `list_resource_types()` can't confirm the type is already installed) globs specifically for `resource-types/fuels-and-electricity-bills.json` and verifies the loaded JSON's own `slug`, raising loudly if more than one installed blueprint's bundle matches — but this sandbox-glob branch is necessarily unexercised by any test in this repo checkout (`/workspace` doesn't exist here). The local dev/test fallback (the human slug `fuels-and-electricity-emissions-service`, a real sibling directory in this checkout) is what every test actually runs against. - Two triaged rows that end up sharing an identical recomputed `reference` (no `billingIdentifier` to distinguish them) are not detected by this skill — the extract skill already prevents this among its own output (`_split_duplicate_references`), but a triage decision could theoretically reintroduce a collision (e.g. two different parked rows both corrected to the same site/type/date with no identifier). Not implemented here; flagged as a residual gap rather than silently accepted. 8/11/2026
Installed bundles are copies — your workspace data stays in your workspace. Qonstrue Community Marketplace