From 36b2120506d3b20952925b99c5e5a7bf683b7450 Mon Sep 17 00:00:00 2001 From: "Juan S. Mrad" Date: Sat, 21 Mar 2026 17:16:51 -0500 Subject: [PATCH 1/3] [Types] Upgrade types and include new required model card fields --- README.md | 2 +- package-lock.json | 16 ++++---- package.json | 6 +-- src/index.ts | 97 +++++++++++++++++++++++++++++++++++++---------- 4 files changed, 89 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index b3db2f3..da70aab 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Restart the COOP server so it loads the plugin. This package implements the COOP plugin contract from `@roostorg/types`: - **Default export:** `CoopIntegrationPlugin` with `manifest` and `createSignals(context)`. -- **Manifest:** `id`, `name`, `version`, `requiresConfig`, `configurationFields`, `signalTypeIds`, `modelCard` (with required sections `modelDetails` and `technicalIntegration`). +- **Manifest:** `id`, `name`, `version`, `requiresConfig`, `configurationFields`, `signalTypeIds`, `modelCard` (must include every section id in `REQUIRED_MODEL_CARD_SECTION_IDS` from `@roostorg/types`; call `assertModelCardHasRequiredSections(modelCard)` when registering). - **createSignals:** Returns two descriptors: - `RANDOM_SIGNAL_SELECTION`: `run(input)` uses `context.getCredential(orgId)` for true percentage; returns `{ outputType: { scalarType: 'BOOLEAN' }, score: boolean }`. - `RANDOM_SCORE`: `run()` returns `{ outputType: { scalarType: 'NUMBER' }, score: number }` in [0, 1]; no config. Threshold is set in the rule (above/below). diff --git a/package-lock.json b/package-lock.json index 50e4a19..807bd67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,22 @@ { "name": "@roostorg/coop-integration-example", - "version": "1.0.0", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@roostorg/coop-integration-example", - "version": "1.0.0", - "license": "ISC", + "version": "2.0.0", + "license": "apache-2.0", "devDependencies": { - "@roostorg/types": "^1.1.1", + "@roostorg/types": "^2.0.0", "typescript": "^5.0.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@roostorg/types": ">=1.0.0" + "@roostorg/types": ">=2.0.0" } }, "node_modules/@babel/runtime": { @@ -30,9 +30,9 @@ } }, "node_modules/@roostorg/types": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@roostorg/types/-/types-1.1.1.tgz", - "integrity": "sha512-NhPYlG27wAQaD7AzWkL3LJHu52/QfK8lt9QMahUx7fbRtB4fYILy4fGcLQvt45gNQANoU78evW1UJftAB0B89Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@roostorg/types/-/types-2.0.0.tgz", + "integrity": "sha512-EyLecshJqiZdughsBgaJcR6nOmZ6cT5ioNeRB1Cif6b0ZrXc9LfBirXd2FPZ1qzxxIjgMUOA1HSxfFVThMRZlw==", "dev": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 6ed011c..a79d7b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@roostorg/coop-integration-example", - "version": "1.0.0", + "version": "2.0.0", "description": "Example package to show how a custom integration and signal can be used in COOP this is meant to be a reference repository and provide basic determination", "type": "module", "main": "dist/index.js", @@ -33,10 +33,10 @@ }, "homepage": "https://github.com/roostorg/coop-integration-example#readme", "peerDependencies": { - "@roostorg/types": ">=1.0.0" + "@roostorg/types": ">=2.0.0" }, "devDependencies": { - "@roostorg/types": "^1.1.1", + "@roostorg/types": "^2.0.0", "typescript": "^5.0.0" }, "engines": { diff --git a/src/index.ts b/src/index.ts index 2f7c9aa..d50e11c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,12 +4,13 @@ * 2. Random Score – numeric [0, 1], threshold set in the rule (tests score vs threshold). */ -import type { - CoopIntegrationPlugin, - IntegrationManifest, - ModelCard, - PluginSignalContext, - PluginSignalDescriptor, +import { + assertModelCardHasRequiredSections, + type CoopIntegrationPlugin, + type IntegrationManifest, + type ModelCard, + type PluginSignalContext, + type PluginSignalDescriptor, } from '@roostorg/types'; const SIGNAL_TYPE_RANDOM_SELECTION = 'RANDOM_SIGNAL_SELECTION'; @@ -20,48 +21,104 @@ const DEFAULT_TRUE_PERCENTAGE = 50; const modelCard: ModelCard = { modelName: 'COOP Integration Example', version: '1.0.0', - releaseDate: '2026', + releaseDate: 'March 2026', sections: [ { - id: 'modelDetails', - title: 'Model Details', + id: 'trainingData', + title: 'Training Data', fields: [ - { label: 'Model Name', value: 'COOP Integration Example' }, { - label: 'Purpose', + label: 'Overview', value: - 'Example plugin with two signals: one uses org config (boolean), one returns a numeric score so you can set a threshold in the rule (over/under).', + 'This reference integration does not use a trained model. Outputs are randomly generated for demonstration and testing of COOP rules, configuration, and UI only.', }, + ], + }, + { + id: 'policyAndTaxonomy', + title: 'Policy and Taxonomy', + fields: [ + { + label: 'Scope', + value: + 'Not a content policy engine. Signals are placeholders: boolean “coin flip” with configurable probability and a numeric random score for threshold exercises in rules.', + }, + ], + }, + { + id: 'annotationMethodology', + title: 'Annotation Methodology', + fields: [ + { + label: 'Method', + value: + 'No human or automated labeling pipeline. Values are produced with Math.random() (or equivalent logic) at evaluation time.', + }, + ], + }, + { + id: 'performanceBenchmarks', + title: 'Performance and Benchmarks', + fields: [ + { + label: 'Benchmarks', + value: + 'No precision, recall, or latency benchmarks apply. Do not use performance claims from this package in production decisions.', + }, + ], + }, + { + id: 'biasAndLimitations', + title: 'Bias and Limitations', + fields: [ + { + label: 'Limitations', + value: + 'Outputs are uncorrelated with input content. Unsuitable for safety, compliance, or moderation decisions. For integration testing and developer learning only.', + }, + ], + }, + { + id: 'implementationGuidance', + title: 'Implementation Guidance', + fields: [ { label: 'Signals', - value: `${SIGNAL_TYPE_RANDOM_SELECTION} (boolean, config-driven) and ${SIGNAL_TYPE_RANDOM_SCORE} (number 0–1, threshold in rule).`, + value: `${SIGNAL_TYPE_RANDOM_SELECTION} (boolean; org config truePercentage 0–100). ${SIGNAL_TYPE_RANDOM_SCORE} (number; set threshold and above/below in the rule).`, + }, + { + label: 'Configuration', + value: + 'Random Signal Selection requires org integration config (true percentage). Random Score requires no integration config.', }, ], }, { - id: 'technicalIntegration', - title: 'Technical Integration', + id: 'relevantLinks', + title: 'Relevant Links', fields: [ { - label: 'Signal types', - value: `${SIGNAL_TYPE_RANDOM_SELECTION}, ${SIGNAL_TYPE_RANDOM_SCORE}`, + label: 'Repository', + value: 'https://github.com/roostorg/coop-integration-example', }, { - label: 'Config', - value: 'truePercentage (0–100) for Random Signal Selection only; Random Score needs no config.', + label: 'Documentation', + value: 'https://roostorg.github.io/coop/INTEGRATIONS_PLUGIN.html', }, ], }, ], }; +assertModelCardHasRequiredSections(modelCard); + const manifest: IntegrationManifest = { id: INTEGRATION_ID, name: 'COOP Integration Example', version: '1.0.0', description: 'Example plugin with two signals: config-driven boolean and a numeric score you compare with a threshold in the rule.', - docsUrl: 'https://github.com/roostorg/coop/tree/main/coop-integration-example', + docsUrl: 'https://roostorg.github.io/coop/INTEGRATIONS_PLUGIN.html', requiresConfig: true, configurationFields: [ { From fccbcf88a8cfdcd4fad69c02c20c575f2f2ea6ae Mon Sep 17 00:00:00 2001 From: "Juan S. Mrad" Date: Sat, 21 Mar 2026 17:19:18 -0500 Subject: [PATCH 2/3] add codeowners --- .github/CODEOWNERS | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..8138941 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,6 @@ +# CODEOWNERS file for managing repository permissions +# Format: path-pattern @username @org/team-name + +# Maintainers: Default owners for all files +* @roostorg/roosters + From ea0a9625b91e45a331cde49101809f31a1215a6f Mon Sep 17 00:00:00 2001 From: "Juan S. Mrad" Date: Mon, 23 Mar 2026 13:29:12 -0500 Subject: [PATCH 3/3] code review changes --- README.md | 6 +++--- package-lock.json | 2 +- package.json | 2 +- src/index.ts | 23 ++++++++++++++--------- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index da70aab..31ebfb1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Example [COOP](https://github.com/roostorg/coop) integration plugin. Reference r - **Integration:** `COOP_INTEGRATION_EXAMPLE` - **Signal 1 – Random Signal Selection** (`RANDOM_SIGNAL_SELECTION`): Returns `true` or `false` at random. The probability (0–100%) comes from the org’s integration config (“True percentage”). Set e.g. `70` in Org settings → Integrations; the signal returns `true` about 70% of the time. Use this to test config saving and boolean conditions. -- **Signal 2 – Random Score** (`RANDOM_SCORE`): Returns a random number between 0 and 1. No integration config needed. In the rule builder you set a **threshold** (e.g. `0.5`) and choose **above** or **below**. Use this to test numeric score conditions (e.g. “score ≥ 0.5” or “score < 0.3”). +- **Signal 2 – Random Score** (`RANDOM_SCORE`): Returns a random number from **0 up to (but not including) 100**—the same 0–100 style as “True percentage,” so thresholds feel consistent. No integration config needed. In the rule builder set a **threshold** (e.g. `50`) and choose **above** or **below**. Use this to test numeric score conditions (e.g. “score ≥ 50” or “score < 30”). ## Install @@ -61,7 +61,7 @@ Restart the COOP server so it loads the plugin. 1. **Org settings → Integrations** – you should see “COOP Integration Example”. Open it and set **True percentage (0–100)** (e.g. `70`) for Random Signal Selection. Save. 2. **Rules (routing or enforcement)** – when adding a condition: - **Random Signal Selection**: Pick that signal; the condition uses your configured percentage (true/false). - - **Random Score**: Pick “Random Score”, then set a **threshold** (e.g. `0.5`) and choose **above** or **below**. The rule compares the random score to your threshold. + - **Random Score**: Pick “Random Score”, then set a **threshold** on the 0–100 scale (e.g. `50`) and choose **above** or **below**. The rule compares the random score to your threshold. ## Contract @@ -71,7 +71,7 @@ This package implements the COOP plugin contract from `@roostorg/types`: - **Manifest:** `id`, `name`, `version`, `requiresConfig`, `configurationFields`, `signalTypeIds`, `modelCard` (must include every section id in `REQUIRED_MODEL_CARD_SECTION_IDS` from `@roostorg/types`; call `assertModelCardHasRequiredSections(modelCard)` when registering). - **createSignals:** Returns two descriptors: - `RANDOM_SIGNAL_SELECTION`: `run(input)` uses `context.getCredential(orgId)` for true percentage; returns `{ outputType: { scalarType: 'BOOLEAN' }, score: boolean }`. - - `RANDOM_SCORE`: `run()` returns `{ outputType: { scalarType: 'NUMBER' }, score: number }` in [0, 1]; no config. Threshold is set in the rule (above/below). + - `RANDOM_SCORE`: `run()` returns `{ outputType: { scalarType: 'NUMBER' }, score: number }` in **[0, 100)** (`Math.random() * 100`); no config. Threshold is set in the rule on the same scale (above/below). ## Publishing diff --git a/package-lock.json b/package-lock.json index 807bd67..2f537f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "@roostorg/coop-integration-example", "version": "2.0.0", - "license": "apache-2.0", + "license": "Apache-2.0", "devDependencies": { "@roostorg/types": "^2.0.0", "typescript": "^5.0.0" diff --git a/package.json b/package.json index a79d7b8..7c4eb91 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "example" ], "author": "Roostorg", - "license": "apache-2.0", + "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/roostorg/coop-integration-example.git" diff --git a/src/index.ts b/src/index.ts index d50e11c..bcf5684 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ /** * Example COOP integration plugin with two signal types: * 1. Random Signal Selection – boolean, probability from org config (tests config saving). - * 2. Random Score – numeric [0, 1], threshold set in the rule (tests score vs threshold). + * 2. Random Score – numeric 0–100, threshold set in the rule (tests score vs threshold). */ import { @@ -20,7 +20,7 @@ const DEFAULT_TRUE_PERCENTAGE = 50; const modelCard: ModelCard = { modelName: 'COOP Integration Example', - version: '1.0.0', + version: '2.0.0', releaseDate: 'March 2026', sections: [ { @@ -84,13 +84,18 @@ const modelCard: ModelCard = { fields: [ { label: 'Signals', - value: `${SIGNAL_TYPE_RANDOM_SELECTION} (boolean; org config truePercentage 0–100). ${SIGNAL_TYPE_RANDOM_SCORE} (number; set threshold and above/below in the rule).`, + value: `${SIGNAL_TYPE_RANDOM_SELECTION} (boolean; org config truePercentage 0–100). ${SIGNAL_TYPE_RANDOM_SCORE} (number 0–100; set threshold and above/below in the rule).`, }, { label: 'Configuration', value: 'Random Signal Selection requires org integration config (true percentage). Random Score requires no integration config.', }, + { + label: 'Versioning', + value: + 'modelCard.version and manifest.version identify this integration plugin release. They are independent of the @roostorg/types dependency major version (e.g. 2.x).', + }, ], }, { @@ -115,7 +120,8 @@ assertModelCardHasRequiredSections(modelCard); const manifest: IntegrationManifest = { id: INTEGRATION_ID, name: 'COOP Integration Example', - version: '1.0.0', + /** Same semver as modelCard.version: this plugin’s release, not @roostorg/types. */ + version: '2.0.0', description: 'Example plugin with two signals: config-driven boolean and a numeric score you compare with a threshold in the rule.', docsUrl: 'https://roostorg.github.io/coop/INTEGRATIONS_PLUGIN.html', @@ -215,11 +221,11 @@ function createRandomScoreDescriptor( id: { type: SIGNAL_TYPE_RANDOM_SCORE }, displayName: 'Random Score', description: - 'Returns a random number between 0 and 1. Set a threshold in the rule (e.g. 0.5) and choose "above" or "below" to test numeric conditions.', + 'Returns a random number from 0 up to (but not including) 100. Set a threshold in the rule (e.g. 50) and choose "above" or "below" to test numeric conditions.', docsUrl: null, recommendedThresholds: { - highPrecisionThreshold: 0.5, - highRecallThreshold: 0.5, + highPrecisionThreshold: 50, + highRecallThreshold: 50, }, supportedLanguages: 'ALL', pricingStructure: { type: 'FREE' }, @@ -235,8 +241,7 @@ function createRandomScoreDescriptor( async run( _input: unknown, ): Promise<{ outputType: typeof outputType; score: number }> { - // Returns a random number between 0 and 100. - // Because outputType is { scalarType: 'NUMBER' }, Coop can take the score and compare it to a threshold in the rule. + // [0, 100) — same scale as percentages elsewhere in this plugin (e.g. truePercentage). const score = Math.random() * 100; return { outputType, score }; },