@whittlelabs/sifter 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/bin.js +81 -2
  2. package/bin.js.map +2 -2
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -15399,9 +15399,26 @@ var require_client = __commonJS({
15399
15399
  async getJobPool(poolId) {
15400
15400
  return this.request("GET", `/api/job-pools/${poolId}`);
15401
15401
  }
15402
+ /**
15403
+ * Resolve a pool by exact name. Names are unique per OWNER, not globally
15404
+ * (whittlelabs#763), and this method lists without a principal filter, so
15405
+ * the same name can legitimately come back under several owners. That
15406
+ * reference is inherently ambiguous — and silently picking one (the old
15407
+ * behavior took the server's newest-first ordering) is exactly the
15408
+ * capture vector the per-owner constraint closed, re-opened cross-owner.
15409
+ * So it refuses instead: reference the pool by id, or list with explicit
15410
+ * principal filters, when more than one owner holds the name.
15411
+ */
15402
15412
  async resolveJobPoolByName(name) {
15403
15413
  const pools = await this.listJobPools({ name });
15404
- return pools.find((p) => p.name === name);
15414
+ const matches = pools.filter((p) => p.name === name);
15415
+ const owners = new Set(matches.map((p) => `${p.principalClass ?? ""}/${p.principalId ?? ""}`));
15416
+ if (owners.size > 1) {
15417
+ const error = new Error(`Job pool name "${name}" is ambiguous: ${matches.length} pools under ${owners.size} different owners hold it. Reference the pool by id, or resolve it with explicit principal filters (listJobPools).`);
15418
+ error.code = "JOB_POOL_NAME_AMBIGUOUS";
15419
+ throw error;
15420
+ }
15421
+ return matches[0];
15405
15422
  }
15406
15423
  /** Internal: resolve a `PoolRef` to a pool id. */
15407
15424
  async resolvePoolRef(ref) {
@@ -19502,10 +19519,45 @@ var require_output_gates = __commonJS({
19502
19519
  "../../packages/shuttle/dist/executors/output-gates.js"(exports2) {
19503
19520
  "use strict";
19504
19521
  Object.defineProperty(exports2, "__esModule", { value: true });
19522
+ exports2.isSanctionedPaint = isSanctionedPaint;
19523
+ exports2.unsanctionedPaints = unsanctionedPaints;
19505
19524
  exports2.resolvePath = resolvePath;
19506
19525
  exports2.evaluateOutputGates = evaluateOutputGates;
19507
19526
  exports2.renderRepairPrompt = renderRepairPrompt;
19508
19527
  var INPUT_REF = "input:";
19528
+ var COLOR_BEARING = ["fill", "stroke", "color", "stop-color", "flood-color", "lighting-color"];
19529
+ var INERT = /* @__PURE__ */ new Set(["none", "inherit", "initial", "unset", "transparent", "currentcolor"]);
19530
+ function isSanctionedPaint(raw) {
19531
+ const value = raw.trim().toLowerCase();
19532
+ if (value === "")
19533
+ return true;
19534
+ if (INERT.has(value))
19535
+ return true;
19536
+ if (/^url\(\s*['"]?#[^)'"]+['"]?\s*\)$/.test(value))
19537
+ return true;
19538
+ const varMatch = /^var\(\s*(--[a-z0-9-]+)\s*(?:,([\s\S]*))?\)$/.exec(value);
19539
+ if (!varMatch)
19540
+ return false;
19541
+ const [, name, fallback] = varMatch;
19542
+ if (!name.startsWith("--color-"))
19543
+ return false;
19544
+ return fallback === void 0 || isSanctionedPaint(fallback);
19545
+ }
19546
+ function unsanctionedPaints(source) {
19547
+ const found = [];
19548
+ const attrs = COLOR_BEARING.join("|");
19549
+ const attribute = new RegExp(`\\b(?:${attrs})\\s*=\\s*(['"])([\\s\\S]*?)\\1`, "gi");
19550
+ const declaration = new RegExp(`\\b(?:${attrs})\\s*:\\s*([^;"'}\\n]+)`, "gi");
19551
+ for (const [, , value] of source.matchAll(attribute)) {
19552
+ if (!isSanctionedPaint(value))
19553
+ found.push(value.trim());
19554
+ }
19555
+ for (const [, value] of source.matchAll(declaration)) {
19556
+ if (!isSanctionedPaint(value))
19557
+ found.push(value.trim());
19558
+ }
19559
+ return found;
19560
+ }
19509
19561
  function resolvePath(root, path) {
19510
19562
  let ctx = root;
19511
19563
  for (const seg of path.split(".")) {
@@ -19593,6 +19645,33 @@ var require_output_gates = __commonJS({
19593
19645
  return [
19594
19646
  `'${path}' must be a non-empty array${cond}; it was ${Array.isArray(value) ? "empty" : "absent"}.`
19595
19647
  ];
19648
+ },
19649
+ /**
19650
+ * `bounded_palette` — every colour in the sources at `path` must be named
19651
+ * by role rather than written as a value. Params:
19652
+ * `{ path: <output path>, roles?: <role names for the repair turn> }`.
19653
+ *
19654
+ * A drawing the model authors is markup the house did not write, and a
19655
+ * literal in it is a colour outside the consuming palette: unproved
19656
+ * against the colours beside it, unchecked for contrast on the ground it
19657
+ * lands on, and fixed to one theme. The consumer strips such a paint at
19658
+ * render, so an artifact that carries one silently loses it. Catching it
19659
+ * HERE is what makes that a repair the model can make while it still has
19660
+ * the drawing in hand, instead of a defect nobody sees until the picture
19661
+ * comes out plain.
19662
+ */
19663
+ bounded_palette(params, output) {
19664
+ const path = String(params.path);
19665
+ const value = resolvePath(output, path);
19666
+ const sources = (Array.isArray(value) ? value : [value]).filter((v) => typeof v === "string" && v.length > 0);
19667
+ const offenders = [...new Set(sources.flatMap(unsanctionedPaints))];
19668
+ if (offenders.length === 0)
19669
+ return [];
19670
+ const roles = Array.isArray(params.roles) ? params.roles.map(String) : [];
19671
+ const vocabulary = roles.length > 0 ? ` Use one of: ${roles.join(", ")}.` : "";
19672
+ return [
19673
+ `'${path}' states ${offenders.length} colour value${offenders.length === 1 ? "" : "s"} directly (${offenders.slice(0, 8).join(", ")}${offenders.length > 8 ? ", \u2026" : ""}). Every colour must be named by role \u2014 var(--color-\u2026), currentColor, or none \u2014 never as a hex, rgb(), hsl() or colour name, because a value cannot adapt to the reader's theme and was never checked against the colours beside it.${vocabulary} Redraw with roles and return the whole output again.`
19674
+ ];
19596
19675
  }
19597
19676
  };
19598
19677
  function evaluateOutputGates(gates, output, inputs) {
@@ -22371,7 +22450,7 @@ var import_path = require("path");
22371
22450
  var import_promises = require("fs/promises");
22372
22451
  var import_yaml = __toESM(require_dist());
22373
22452
  var import_shuttle = __toESM(require_dist5());
22374
- var buildVersion = true ? "0.25.0" : pkg.version;
22453
+ var buildVersion = true ? "0.27.0" : pkg.version;
22375
22454
  var sifterBrand = {
22376
22455
  product: {
22377
22456
  id: "sifter",