Changeset 3738042
- Timestamp:
- 10/10/2026 12:21:38 PM (10 days ago)
- Location:
- kairoseth-ai-transparency
- Files:
-
- 14 added
- 28 edited
- 1 copied
-
tags/1.2.0 (copied) (copied from kairoseth-ai-transparency/trunk )
-
tags/1.2.0/ai-transparency.php (modified) (2 diffs)
-
tags/1.2.0/assets/admin-v11.css (added)
-
tags/1.2.0/assets/admin-v11.js (added)
-
tags/1.2.0/assets/admin.css (modified) (2 diffs)
-
tags/1.2.0/readme.txt (modified) (13 diffs)
-
tags/1.2.0/src/Admin/class-adminnavigation.php (added)
-
tags/1.2.0/src/Admin/class-adminpage.php (modified) (7 diffs)
-
tags/1.2.0/src/Admin/class-dashboardpage.php (added)
-
tags/1.2.0/src/Admin/class-disclosurepage.php (modified) (1 diff)
-
tags/1.2.0/src/Admin/class-discoverypage.php (modified) (10 diffs)
-
tags/1.2.0/src/Admin/class-evidenceexportpage.php (modified) (1 diff)
-
tags/1.2.0/src/Admin/class-readinesspage.php (modified) (3 diffs)
-
tags/1.2.0/src/Discovery/class-aienginedetector.php (modified) (1 diff)
-
tags/1.2.0/src/Discovery/class-aipufferdetector.php (added)
-
tags/1.2.0/src/Discovery/class-detectorinterface.php (added)
-
tags/1.2.0/src/Discovery/class-discoveryengine.php (added)
-
tags/1.2.0/src/Domain/class-aisystem.php (modified) (6 diffs)
-
tags/1.2.0/src/Evidence/class-finding.php (modified) (1 diff)
-
tags/1.2.0/src/Evidence/class-findingengine.php (modified) (3 diffs)
-
tags/1.2.0/src/Registry/class-registryschema.php (modified) (3 diffs)
-
tags/1.2.0/src/class-plugin.php (modified) (2 diffs)
-
trunk/ai-transparency.php (modified) (2 diffs)
-
trunk/assets/admin-v11.css (added)
-
trunk/assets/admin-v11.js (added)
-
trunk/assets/admin.css (modified) (2 diffs)
-
trunk/readme.txt (modified) (13 diffs)
-
trunk/src/Admin/class-adminnavigation.php (added)
-
trunk/src/Admin/class-adminpage.php (modified) (7 diffs)
-
trunk/src/Admin/class-dashboardpage.php (added)
-
trunk/src/Admin/class-disclosurepage.php (modified) (1 diff)
-
trunk/src/Admin/class-discoverypage.php (modified) (10 diffs)
-
trunk/src/Admin/class-evidenceexportpage.php (modified) (1 diff)
-
trunk/src/Admin/class-readinesspage.php (modified) (3 diffs)
-
trunk/src/Discovery/class-aienginedetector.php (modified) (1 diff)
-
trunk/src/Discovery/class-aipufferdetector.php (added)
-
trunk/src/Discovery/class-detectorinterface.php (added)
-
trunk/src/Discovery/class-discoveryengine.php (added)
-
trunk/src/Domain/class-aisystem.php (modified) (6 diffs)
-
trunk/src/Evidence/class-finding.php (modified) (1 diff)
-
trunk/src/Evidence/class-findingengine.php (modified) (3 diffs)
-
trunk/src/Registry/class-registryschema.php (modified) (3 diffs)
-
trunk/src/class-plugin.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
kairoseth-ai-transparency/tags/1.2.0/ai-transparency.php
r3735383 r3738042 4 4 * Plugin URI: https://kairoseth.com/products/ai-transparency 5 5 * Description: EU AI Act transparency readiness tooling for WordPress, with local AI system inventory, evidence and disclosure workflows. 6 * Version: 1. 0.36 * Version: 1.2.0 7 7 * Requires at least: 6.6 8 8 * Requires PHP: 7.4 … … 20 20 } 21 21 22 define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1. 0.3' );22 define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1.2.0' ); 23 23 define( 'KAIROSETH_AI_TRANSPARENCY_FILE', __FILE__ ); 24 24 define( 'KAIROSETH_AI_TRANSPARENCY_PATH', plugin_dir_path( __FILE__ ) ); -
kairoseth-ai-transparency/tags/1.2.0/assets/admin.css
r3735383 r3738042 123 123 max-width: 760px; 124 124 font-style: italic; 125 } 126 127 .kat-dashboard { 128 max-width: 1280px; 129 } 130 131 .kat-dashboard-hero { 132 display: flex; 133 align-items: center; 134 justify-content: space-between; 135 gap: 24px; 136 margin: 20px 0 24px; 137 padding: 30px; 138 border: 1px solid #184fb1; 139 border-radius: 16px; 140 background: linear-gradient(135deg, #081a45 0%, #0b2c68 48%, #123b8d 100%); 141 box-shadow: 0 12px 32px rgba(8, 26, 69, 0.16); 142 color: #fff; 143 } 144 145 .kat-dashboard-hero h1 { 146 margin: 4px 0 8px; 147 color: #fff; 148 font-size: clamp(28px, 4vw, 42px); 149 line-height: 1.08; 150 } 151 152 .kat-dashboard-hero p { 153 max-width: 760px; 154 margin: 0; 155 color: #d9e8ff; 156 font-size: 15px; 157 } 158 159 .kat-dashboard-kicker { 160 font-size: 12px !important; 161 font-weight: 700; 162 letter-spacing: .12em; 163 text-transform: uppercase; 164 color: #66e4ff !important; 165 } 166 167 .kat-dashboard-version { 168 min-width: 130px; 169 padding: 14px 16px; 170 border: 1px solid rgba(255, 255, 255, .18); 171 border-radius: 12px; 172 background: rgba(255, 255, 255, .08); 173 text-align: center; 174 } 175 176 .kat-dashboard-version span, 177 .kat-dashboard-version strong { 178 display: block; 179 } 180 181 .kat-dashboard-version span { 182 margin-bottom: 4px; 183 font-size: 12px; 184 color: #bcd5ff; 185 } 186 187 .kat-dashboard-version strong { 188 font-size: 22px; 189 color: #fff; 190 } 191 192 .kat-dashboard-metrics { 193 display: grid; 194 grid-template-columns: repeat(4, minmax(0, 1fr)); 195 gap: 16px; 196 margin-bottom: 20px; 197 } 198 199 .kat-dashboard-metric { 200 display: flex; 201 align-items: center; 202 gap: 14px; 203 min-width: 0; 204 padding: 18px; 205 border: 1px solid #d8e3f5; 206 border-radius: 12px; 207 background: #fff; 208 box-shadow: 0 4px 14px rgba(17, 47, 95, .06); 209 } 210 211 .kat-dashboard-metric > .dashicons { 212 width: 42px; 213 height: 42px; 214 border-radius: 10px; 215 background: #eaf5ff; 216 color: #0f72cf; 217 font-size: 23px; 218 line-height: 42px; 219 text-align: center; 220 } 221 222 .kat-dashboard-metric strong, 223 .kat-dashboard-metric span { 224 display: block; 225 } 226 227 .kat-dashboard-metric strong { 228 font-size: 26px; 229 line-height: 1; 230 color: #10213f; 231 } 232 233 .kat-dashboard-metric div > span { 234 margin-top: 5px; 235 color: #52637d; 236 } 237 238 .kat-dashboard-status { 239 display: flex; 240 align-items: center; 241 justify-content: space-between; 242 gap: 24px; 243 margin: 0 0 28px; 244 padding: 20px 22px; 245 border: 1px solid #cfe6ef; 246 border-left: 4px solid #16a6c9; 247 border-radius: 10px; 248 background: #f8fdff; 249 } 250 251 .kat-dashboard-status h2 { 252 margin: 0 0 8px; 253 font-size: 18px; 254 } 255 256 .kat-dashboard-status-list { 257 margin: 0; 258 padding-left: 20px; 259 } 260 261 .kat-dashboard-status-list li { 262 margin: 4px 0; 263 } 264 265 .kat-dashboard-section-title { 266 margin: 0 0 14px; 267 font-size: 20px; 268 } 269 270 .kat-dashboard-grid { 271 display: grid; 272 grid-template-columns: repeat(3, minmax(0, 1fr)); 273 gap: 16px; 274 } 275 276 .kat-dashboard-card { 277 display: flex; 278 flex-direction: column; 279 min-width: 0; 280 min-height: 210px; 281 padding: 22px; 282 border: 1px solid #d7e2f1; 283 border-radius: 14px; 284 background: linear-gradient(180deg, #fff 0%, #f8fbff 100%); 285 box-shadow: 0 5px 18px rgba(17, 47, 95, .06); 286 } 287 288 .kat-dashboard-card-icon { 289 display: inline-flex; 290 align-items: center; 291 justify-content: center; 292 width: 44px; 293 height: 44px; 294 margin-bottom: 14px; 295 border-radius: 11px; 296 background: linear-gradient(135deg, #e2f9ff, #eef0ff); 297 color: #176fd1; 298 } 299 300 .kat-dashboard-card-icon .dashicons { 301 width: 24px; 302 height: 24px; 303 font-size: 24px; 304 } 305 306 .kat-dashboard-card h3 { 307 margin: 0 0 8px; 308 font-size: 17px; 309 color: #10213f; 310 } 311 312 .kat-dashboard-card p { 313 flex: 1; 314 margin: 0 0 18px; 315 color: #52637d; 316 line-height: 1.55; 317 } 318 319 .kat-dashboard-card-link { 320 align-self: flex-start; 321 font-weight: 600; 322 text-decoration: none; 323 } 324 325 .kat-dashboard-card-link:focus-visible { 326 outline: 2px solid currentColor; 327 outline-offset: 3px; 328 } 329 330 .kat-dashboard-boundary { 331 max-width: 920px; 332 margin: 24px 0 0; 333 color: #59677a; 334 } 335 336 @media screen and (max-width: 1100px) { 337 .kat-dashboard-metrics { 338 grid-template-columns: repeat(2, minmax(0, 1fr)); 339 } 340 341 .kat-dashboard-grid { 342 grid-template-columns: repeat(2, minmax(0, 1fr)); 343 } 125 344 } 126 345 … … 175 394 min-height: 44px; 176 395 } 177 } 396 397 .kat-dashboard-hero, 398 .kat-dashboard-status { 399 align-items: flex-start; 400 flex-direction: column; 401 } 402 403 .kat-dashboard-version { 404 text-align: left; 405 } 406 407 .kat-dashboard-metrics, 408 .kat-dashboard-grid { 409 grid-template-columns: minmax(0, 1fr); 410 } 411 412 .kat-dashboard-card { 413 min-height: 0; 414 } 415 } -
kairoseth-ai-transparency/tags/1.2.0/readme.txt
r3735383 r3738042 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1. 0.37 Stable tag: 1.2.0 8 8 License: MIT 9 9 License URI: https://opensource.org/license/mit/ … … 17 17 The stable release includes: 18 18 19 * a local AI Systems Registry under **Tools > AI Transparency**; 20 * deterministic AI integration discovery under **Tools > AI Discovery**; 21 * deterministic technical readiness findings under **Tools > AI Readiness**; 22 * explicit administrator-controlled disclosure readiness under **Tools > AI Disclosure**; 23 * privileged local JSON evidence export under **Tools > AI Evidence Export**; 24 * an optional, administrator-initiated Kairoseth support bridge under **Tools > AI Transparency Support**. 25 26 An authorized administrator can add, edit, review and archive AI system records. Registry data is stored locally in the current WordPress site's Options storage with a versioned schema. 27 28 The first validated discovery detector supports **AI Engine 3.7.7** from bounded WordPress plugin identity evidence. Discovery does not inspect AI provider credentials, provider/model settings, prompts, conversations or AI Engine internal configuration. Supported discovery results require explicit administrator acceptance and enter the Registry as pending review. 19 * a dedicated **Kairoseth AI Transparency** admin dashboard with live Registry, review, readiness and disclosure-ready metrics; 20 * a local AI Systems Registry under **Kairoseth AI Transparency > AI Systems Registry**, including instant local search and filters by type, review and lifecycle status; 21 * administrator-authored context for purpose, provider, model/model family, audience, human oversight and data-use context; 22 * deterministic AI integration discovery under **Kairoseth AI Transparency > AI Discovery**; 23 * deterministic technical readiness findings under **Kairoseth AI Transparency > AI Readiness**; 24 * explicit administrator-controlled disclosure readiness under **Kairoseth AI Transparency > AI Disclosure**; 25 * privileged local JSON evidence export under **Kairoseth AI Transparency > AI Evidence Export**; 26 * an optional, administrator-initiated Kairoseth support bridge under **Kairoseth AI Transparency > AI Transparency Support**. 27 28 Legacy Tools URLs from the 1.0.x line remain registered for backwards compatibility, while the visible workflow is grouped under the dedicated Kairoseth AI Transparency menu. 29 30 An authorized administrator can add, edit, review and archive AI system records. Registry data is stored locally in the current WordPress site's Options storage with a versioned schema. Version 1.2 upgrades Registry persistence to schema v2 and migrates earlier records without inventing provider, model, purpose or other administrator declarations. 31 32 AI Discovery uses an extensible deterministic detector architecture. The validated 1.2 detectors support: 33 34 * **AI Engine 3.7.7**; 35 * **AI Puffer 2.4.97** (`gpt3-ai-content-generator/gpt3-ai-content-generator.php`). 36 37 Discovery evaluates bounded WordPress plugin identity, version and activation-state evidence. It does not inspect AI provider credentials, prompts, conversations or private provider configuration, and it does not infer purpose, provider, model, audience, oversight or data-use declarations. Supported discovery results require explicit administrator acceptance and enter the Registry as pending review. 29 38 30 39 AI Readiness generates findings on demand from the current Registry and keeps three concepts separate: … … 33 42 * **Administrator declaration** — an explicit administrator state when relevant; 34 43 * **Guidance** — a technical review or completion action. 44 45 Version 1.2 can also flag a reviewed active record when one or more administrator-context fields remain undocumented. This is a bounded completeness signal, not a legal-compliance decision. 35 46 36 47 AI Disclosure renders only from server-authoritative reviewed Registry state. The public shortcode is: … … 48 59 * a deterministic SHA-256 `snapshot_signature` that excludes generation time; 49 60 * unchanged technical state produces the same signature across export times; 50 * meaningful exported Registry changes change the signature;61 * meaningful exported Registry changes, including administrator-authored context changes, change the signature; 51 62 * archived Registry records are retained; 52 63 * an empty Registry is valid; … … 55 66 * no arbitrary WordPress/plugin option dump or automatic export of credentials, tokens, cookies, nonces, user identities, prompts, conversations, logs or database dumps. 56 67 57 The privileged export may contain administrator-authored `interaction_context`, so the file may contain confidential operational contextand should be handled accordingly.68 The privileged export may contain administrator-authored operational context from the Registry, including interaction context and the 1.2 context fields, so the file may contain confidential business information and should be handled accordingly. 58 69 59 70 **Important:** This plugin provides technical readiness, workflow and evidence tooling. It does not certify or guarantee compliance with the EU AI Act or any other law. The evidence `snapshot_signature` is a technical snapshot identity, not a legal/digital signature, trusted timestamp, non-repudiation proof or regulatory certification. … … 65 76 The plugin is local-first. Registry, Discovery, Readiness, Disclosure and Evidence Export do not require Kairoseth or automatically send their state to an external service. 66 77 67 **Kairoseth Custom Requests** is an optional support/custom-integration service used only after an administrator deliberately clicks a link on ** Tools> AI Transparency Support**. Loading the WordPress support page itself makes no Kairoseth request.78 **Kairoseth Custom Requests** is an optional support/custom-integration service used only after an administrator deliberately clicks a link on **Kairoseth AI Transparency > AI Transparency Support**. Loading the WordPress support page itself makes no Kairoseth request. 68 79 69 80 The explicit browser navigation goes to `https://kairoseth.com/custom-requests` and includes only this bounded technical/product context in the URL: … … 78 89 * the administrator-selected bounded request type. 79 90 80 The plugin does **not** automatically attach or transmit the site URL, administrator identity, Registry contents, AI system names, interaction context, Discovery evidence, Readiness findings, Disclosure state, Evidence Export JSON/signature, plugin/theme inventory, server paths, credentials, prompts, conversations, logs or database contents.91 The plugin does **not** automatically attach or transmit the site URL, administrator identity, Registry contents, AI system names, interaction context, administrator context fields, Discovery evidence, Readiness findings, Disclosure state, Evidence Export JSON/signature, plugin/theme inventory, server paths, credentials, prompts, conversations, logs or database contents. 81 92 82 93 After reaching Kairoseth, the administrator decides what contact, business or request information to enter and submit. Kairoseth owns the request form, its consent flow and final submission. … … 90 101 1. Upload the plugin directory to `/wp-content/plugins/` or install the packaged ZIP. 91 102 2. Activate **Kairoseth AI Transparency** from the Plugins screen. 92 3. Open **Tools > AI Transparency** to maintain the local Registry. 93 4. Open **Tools > AI Discovery** to review supported deterministic integration evidence. 94 5. Open **Tools > AI Readiness** to review current technical findings. 95 6. Open **Tools > AI Disclosure** to review disclosure readiness and copy a shortcode for an eligible system. 96 7. Open **Tools > AI Evidence Export** to download the current site-local JSON evidence snapshot. 97 8. Optionally open **Tools > AI Transparency Support** for an explicit privacy-bounded handoff to Kairoseth support/custom requests. 103 3. Open **Kairoseth AI Transparency** for the overview dashboard and current technical status. 104 4. Open **Kairoseth AI Transparency > AI Systems Registry** to maintain, search and filter the local Registry. 105 5. Open **Kairoseth AI Transparency > AI Discovery** to review supported deterministic integration evidence. 106 6. Open **Kairoseth AI Transparency > AI Readiness** to review current technical findings. 107 7. Open **Kairoseth AI Transparency > AI Disclosure** to review disclosure readiness and copy a shortcode for an eligible system. 108 8. Open **Kairoseth AI Transparency > AI Evidence Export** to download the current site-local JSON evidence snapshot. 109 9. Optionally open **Kairoseth AI Transparency > AI Transparency Support** for an explicit privacy-bounded handoff to Kairoseth support/custom requests. 98 110 99 111 == Frequently Asked Questions == … … 109 121 = What does AI Discovery detect? = 110 122 111 The first validated detector recognizes an active AI Engine 3.7.7 installation from WordPress plugin inventory evidence. It does not infer which AI provider, model, chatbot or workflow is actually configured. 123 Version 1.2 includes validated deterministic detectors for AI Engine 3.7.7 and AI Puffer 2.4.97. Discovery identifies only supported plugin identity/version/activation evidence and does not infer which provider, model, purpose, audience or workflow is actually configured. 124 125 = What information can I add to the Registry in 1.2? = 126 127 In addition to the existing technical record, an administrator can document purpose, provider, model/model family, audience, human oversight and data-use context. These values remain explicit administrator declarations and are not filled automatically by Discovery. 112 128 113 129 = What does AI Readiness report? = 114 130 115 The readiness engine reports reproducible technical review conditions derived from the local AI Systems Registry. It keeps observed facts, administrator declarations and technical guidance separate anddoes not make legal compliance decisions.131 The readiness engine reports reproducible technical review conditions derived from the local AI Systems Registry. It keeps observed facts, administrator declarations and technical guidance separate. In 1.2 it can also highlight reviewed active records with incomplete administrator context. It does not make legal compliance decisions. 116 132 117 133 = What is the Evidence Export snapshot signature? = 118 134 119 It is a SHA-256 identity of the bounded stable technical snapshot. Generation time is excluded so repeated exports of unchanged technical state keep the same signature. It is not a legal/digital signature or external timestamp.135 It is a SHA-256 identity of the bounded stable technical snapshot. Generation time is excluded so repeated exports of unchanged technical state keep the same signature. Meaningful Registry changes, including administrator-context changes, change the signature. It is not a legal/digital signature or external timestamp. 120 136 121 137 = Is Evidence Export stored or uploaded? = … … 123 139 No. Evidence Export v1 is built for the explicit administrator request and returned as a direct JSON attachment. The plugin does not persist the generated file or upload it to Kairoseth. 124 140 141 = What happens to my Registry if I update from an earlier version? = 142 143 Version 1.2 migrates earlier Registry records to schema v2 on first access while preserving existing lifecycle, review, disclosure and interaction-context state. New administrator-context fields begin empty unless an administrator explicitly documents them. 144 125 145 = What happens to my Registry if I deactivate or remove the plugin? = 126 146 … … 129 149 = How is Multisite handled? = 130 150 131 Registry and Evidence Export follow the authoritative current blog/site context. The firstexport does not aggregate the full network. A network uninstall removes only the plugin-owned Registry option from each existing site.151 Registry and Evidence Export follow the authoritative current blog/site context. The export does not aggregate the full network. A network uninstall removes only the plugin-owned Registry option from each existing site. 132 152 133 153 = Does it detect whether any article was written by AI? = … … 136 156 137 157 == Changelog == 158 159 = 1.2.0 = 160 * Adds an extensible multi-detector Discovery engine while preserving server-authoritative, deterministic evidence selection. 161 * Adds validated deterministic Discovery support for AI Puffer 2.4.97 alongside AI Engine 3.7.7. 162 * Upgrades the site-local Registry to schema v2 with administrator-authored purpose, provider, model/model family, audience, human oversight and data-use context. 163 * Migrates earlier Registry records safely with the new declaration fields empty instead of inferring values from plugin identity or Discovery evidence. 164 * Adds Registry form controls for all six administrator-context fields with explicit local-first and declaration boundaries. 165 * Extends deterministic Evidence Export snapshots so administrator-context changes participate in the stable snapshot identity. 166 * Adds a bounded Readiness finding for reviewed active systems whose administrator context remains incomplete. 167 * Preserves Fact / Administrator declaration / Guidance separation and does not convert completeness findings into legal-compliance claims. 168 * Extends real WordPress, Multisite, accessibility, migration, exact-ZIP lifecycle and EN/ES validation for the 1.2 data model. 169 170 = 1.1.0 = 171 * Adds a dedicated top-level Kairoseth AI Transparency dashboard with live Registry, review, readiness and disclosure-ready metrics. 172 * Groups Registry, Discovery, Readiness, Disclosure, Evidence Export and Support under one visible plugin menu while preserving legacy 1.0.x Tools URLs for backwards compatibility. 173 * Adds a recent Registry activity table with direct edit actions from the dashboard. 174 * Adds instant local Registry search and filters by system type, review status and lifecycle status without changing the persistence schema. 175 * Improves admin cards, tables, forms, findings, responsive behavior, keyboard focus handling and reduced-motion behavior. 176 * Aligns Spanish source-catalog metadata and the translation template with version 1.1.0 and the canonical `kairoseth-ai-transparency` text domain. 177 * Keeps Registry storage, Evidence Export schema and existing 1.0.x site-local data compatible with the 1.0.x line. 138 178 139 179 = 1.0.3 = … … 170 210 == Upgrade Notice == 171 211 212 = 1.2.0 = 213 Adds multi-detector Discovery, AI Puffer support and Registry schema v2 with explicit administrator context. Existing Registry records are preserved and migrated safely with new declaration fields empty until reviewed. 214 215 = 1.1.0 = 216 Adds the unified Kairoseth AI Transparency dashboard, admin navigation and Registry search/filter UX while preserving the existing site-local Registry schema and 1.0.x data. 217 172 218 = 1.0.3 = 173 219 Ownership-metadata clarification for the ongoing WordPress.org review. No runtime behavior or stored-data schema changes. -
kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-adminpage.php
r3735383 r3738042 125 125 'system' => $system->id(), 126 126 ), 127 admin_url( ' tools.php' )127 admin_url( 'admin.php' ) 128 128 ); 129 129 ?> … … 172 172 check_admin_referer( 'kairoseth_ai_transparency_save_system', '_kat_nonce' ); 173 173 174 $registry = $this->repository->load(); 175 $id = isset( $_POST['system_id'] ) ? sanitize_text_field( wp_unslash( $_POST['system_id'] ) ) : ''; 176 $name = isset( $_POST['system_name'] ) ? sanitize_text_field( wp_unslash( $_POST['system_name'] ) ) : ''; 177 $type = isset( $_POST['system_type'] ) ? sanitize_key( wp_unslash( $_POST['system_type'] ) ) : ''; 178 $context = isset( $_POST['interaction_context'] ) ? sanitize_textarea_field( wp_unslash( $_POST['interaction_context'] ) ) : ''; 179 $review = isset( $_POST['review_status'] ) ? sanitize_key( wp_unslash( $_POST['review_status'] ) ) : AiSystem::REVIEW_PENDING; 180 $required = isset( $_POST['interaction_disclosure_required'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['interaction_disclosure_required'] ) ); 174 $registry = $this->repository->load(); 175 $id = isset( $_POST['system_id'] ) ? sanitize_text_field( wp_unslash( $_POST['system_id'] ) ) : ''; 176 $name = isset( $_POST['system_name'] ) ? sanitize_text_field( wp_unslash( $_POST['system_name'] ) ) : ''; 177 $type = isset( $_POST['system_type'] ) ? sanitize_key( wp_unslash( $_POST['system_type'] ) ) : ''; 178 $context = isset( $_POST['interaction_context'] ) ? sanitize_textarea_field( wp_unslash( $_POST['interaction_context'] ) ) : ''; 179 $purpose = isset( $_POST['purpose'] ) ? sanitize_textarea_field( wp_unslash( $_POST['purpose'] ) ) : ''; 180 $provider = isset( $_POST['provider'] ) ? sanitize_text_field( wp_unslash( $_POST['provider'] ) ) : ''; 181 $model = isset( $_POST['model'] ) ? sanitize_text_field( wp_unslash( $_POST['model'] ) ) : ''; 182 $audience = isset( $_POST['audience'] ) ? sanitize_textarea_field( wp_unslash( $_POST['audience'] ) ) : ''; 183 $oversight = isset( $_POST['human_oversight'] ) ? sanitize_textarea_field( wp_unslash( $_POST['human_oversight'] ) ) : ''; 184 $data_use = isset( $_POST['data_use_context'] ) ? sanitize_textarea_field( wp_unslash( $_POST['data_use_context'] ) ) : ''; 185 $review = isset( $_POST['review_status'] ) ? sanitize_key( wp_unslash( $_POST['review_status'] ) ) : AiSystem::REVIEW_PENDING; 186 $required = isset( $_POST['interaction_disclosure_required'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['interaction_disclosure_required'] ) ); 181 187 182 188 if ( '' === $name || ! in_array( $type, AiSystem::supported_types(), true ) || ! in_array( $review, AiSystem::supported_review_statuses(), true ) ) { … … 191 197 $now = gmdate( 'c' ); 192 198 $reviewed_at = AiSystem::REVIEW_REVIEWED === $review ? $now : ''; 199 $changes = array( 200 'name' => $name, 201 'type' => $type, 202 'review_status' => $review, 203 'interaction_context' => $context, 204 'purpose' => $purpose, 205 'provider' => $provider, 206 'model' => $model, 207 'audience' => $audience, 208 'human_oversight' => $oversight, 209 'data_use_context' => $data_use, 210 'interaction_disclosure_required' => $required, 211 'updated_at' => $now, 212 'reviewed_at' => $reviewed_at, 213 ); 193 214 194 215 if ( $existing ) { 195 $system = $existing->with_updates( 196 array( 197 'name' => $name, 198 'type' => $type, 199 'review_status' => $review, 200 'interaction_context' => $context, 201 'interaction_disclosure_required' => $required, 202 'updated_at' => $now, 203 'reviewed_at' => $reviewed_at, 204 ) 205 ); 216 $system = $existing->with_updates( $changes ); 206 217 } else { 207 218 $system = new AiSystem( … … 211 222 'manual', 212 223 $required, 213 array( 214 'source_origin' => AiSystem::SOURCE_MANUAL, 215 'status' => AiSystem::STATUS_ACTIVE, 216 'review_status' => $review, 217 'interaction_context' => $context, 218 'created_at' => $now, 219 'updated_at' => $now, 220 'reviewed_at' => $reviewed_at, 224 array_merge( 225 $changes, 226 array( 227 'source_origin' => AiSystem::SOURCE_MANUAL, 228 'status' => AiSystem::STATUS_ACTIVE, 229 'created_at' => $now, 230 ) 221 231 ) 222 232 ); … … 272 282 */ 273 283 private function render_form( $system ): void { 274 $name = $system ? $system->name() : ''; 275 $type = $system ? $system->type() : AiSystem::TYPE_CHATBOT; 276 $context = $system ? $system->interaction_context() : ''; 277 $review = $system ? $system->review_status() : AiSystem::REVIEW_PENDING; 278 $required = $system ? $system->interaction_disclosure_required() : false; 284 $name = $system ? $system->name() : ''; 285 $type = $system ? $system->type() : AiSystem::TYPE_CHATBOT; 286 $context = $system ? $system->interaction_context() : ''; 287 $purpose = $system ? $system->purpose() : ''; 288 $provider = $system ? $system->provider() : ''; 289 $model = $system ? $system->model() : ''; 290 $audience = $system ? $system->audience() : ''; 291 $oversight = $system ? $system->human_oversight() : ''; 292 $data_use = $system ? $system->data_use_context() : ''; 293 $review = $system ? $system->review_status() : AiSystem::REVIEW_PENDING; 294 $required = $system ? $system->interaction_disclosure_required() : false; 279 295 ?> 280 296 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> … … 296 312 <?php endforeach; ?> 297 313 </select> 314 </td> 315 </tr> 316 <tr class="kat-form-section"> 317 <td colspan="2"> 318 <h3><?php echo esc_html__( 'Administrator context', 'kairoseth-ai-transparency' ); ?></h3> 319 <p class="description"><?php echo esc_html__( 'These fields are administrator declarations. Discovery does not infer them from plugin identity or version.', 'kairoseth-ai-transparency' ); ?></p> 320 </td> 321 </tr> 322 <tr> 323 <th scope="row"><label for="kat-purpose"><?php echo esc_html__( 'Purpose', 'kairoseth-ai-transparency' ); ?></label></th> 324 <td> 325 <textarea name="purpose" id="kat-purpose" class="large-text" rows="2" aria-describedby="kat-purpose-description"><?php echo esc_textarea( $purpose ); ?></textarea> 326 <p class="description" id="kat-purpose-description"><?php echo esc_html__( 'Describe what this AI system is used for in this site or workflow.', 'kairoseth-ai-transparency' ); ?></p> 327 </td> 328 </tr> 329 <tr> 330 <th scope="row"><label for="kat-provider"><?php echo esc_html__( 'Provider', 'kairoseth-ai-transparency' ); ?></label></th> 331 <td><input name="provider" id="kat-provider" type="text" class="regular-text" value="<?php echo esc_attr( $provider ); ?>"></td> 332 </tr> 333 <tr> 334 <th scope="row"><label for="kat-model"><?php echo esc_html__( 'Model or model family', 'kairoseth-ai-transparency' ); ?></label></th> 335 <td><input name="model" id="kat-model" type="text" class="regular-text" value="<?php echo esc_attr( $model ); ?>"></td> 336 </tr> 337 <tr> 338 <th scope="row"><label for="kat-audience"><?php echo esc_html__( 'Audience', 'kairoseth-ai-transparency' ); ?></label></th> 339 <td> 340 <textarea name="audience" id="kat-audience" class="large-text" rows="2" aria-describedby="kat-audience-description"><?php echo esc_textarea( $audience ); ?></textarea> 341 <p class="description" id="kat-audience-description"><?php echo esc_html__( 'Describe the people who use, receive, or are affected by this system.', 'kairoseth-ai-transparency' ); ?></p> 342 </td> 343 </tr> 344 <tr> 345 <th scope="row"><label for="kat-human-oversight"><?php echo esc_html__( 'Human oversight', 'kairoseth-ai-transparency' ); ?></label></th> 346 <td> 347 <textarea name="human_oversight" id="kat-human-oversight" class="large-text" rows="3" aria-describedby="kat-human-oversight-description"><?php echo esc_textarea( $oversight ); ?></textarea> 348 <p class="description" id="kat-human-oversight-description"><?php echo esc_html__( 'Describe who reviews, approves, or can intervene in the system outputs or decisions.', 'kairoseth-ai-transparency' ); ?></p> 349 </td> 350 </tr> 351 <tr> 352 <th scope="row"><label for="kat-data-use-context"><?php echo esc_html__( 'Data use context', 'kairoseth-ai-transparency' ); ?></label></th> 353 <td> 354 <textarea name="data_use_context" id="kat-data-use-context" class="large-text" rows="3" aria-describedby="kat-data-use-context-description"><?php echo esc_textarea( $data_use ); ?></textarea> 355 <p class="description" id="kat-data-use-context-description"><?php echo esc_html__( 'Describe the categories of data the workflow uses or receives. Do not enter secrets or credentials.', 'kairoseth-ai-transparency' ); ?></p> 298 356 </td> 299 357 </tr> … … 380 438 'kat_notice' => $notice, 381 439 ), 382 admin_url( ' tools.php' )440 admin_url( 'admin.php' ) 383 441 ) 384 442 ); -
kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-disclosurepage.php
r3735383 r3738042 119 119 'system' => $system->id(), 120 120 ), 121 admin_url( ' tools.php' )121 admin_url( 'admin.php' ) 122 122 ); 123 123 $shortcode = sprintf( '[kairoseth_ai_disclosure system="%s"]', $system->id() ); -
kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-discoverypage.php
r3735383 r3738042 9 9 10 10 use Kairoseth\AITransparency\Discovery\AiEngineDetector; 11 use Kairoseth\AITransparency\Discovery\AiPufferDetector; 12 use Kairoseth\AITransparency\Discovery\DiscoveryEngine; 11 13 use Kairoseth\AITransparency\Discovery\DiscoveryResult; 12 14 use Kairoseth\AITransparency\Discovery\WordPressPluginInventory; … … 32 34 33 35 /** 34 * First validated deterministic detector.35 * 36 * @var AiEngineDetector37 */ 38 private $ ai_engine_detector;36 * Deterministic detector orchestration engine. 37 * 38 * @var DiscoveryEngine 39 */ 40 private $discovery_engine; 39 41 40 42 /** … … 42 44 */ 43 45 public function __construct() { 44 $this->repository = new WordPressOptionsRegistryRepository(); 45 $this->inventory = new WordPressPluginInventory(); 46 $this->ai_engine_detector = new AiEngineDetector(); 46 $this->repository = new WordPressOptionsRegistryRepository(); 47 $this->inventory = new WordPressPluginInventory(); 48 $this->discovery_engine = new DiscoveryEngine( 49 array( 50 new AiEngineDetector(), 51 new AiPufferDetector(), 52 ) 53 ); 47 54 } 48 55 … … 100 107 $this->require_permission(); 101 108 102 $result = $this->detect_ai_engine(); 103 $registry = $this->repository->load(); 104 $candidate = $result && $result->supported() ? $result->candidate( gmdate( 'c' ) ) : null; 105 $existing = $candidate ? $registry->find( $candidate->id() ) : null; 109 $results = $this->discover_results(); 110 $registry = $this->repository->load(); 106 111 ?> 107 112 <div class="wrap ai-transparency-admin"> … … 114 119 <h2><?php echo esc_html__( 'Deterministic discovery', 'kairoseth-ai-transparency' ); ?></h2> 115 120 116 <?php if ( null === $result) : ?>117 <p><?php echo esc_html__( ' AI Engine was not detected as active in theWordPress plugin inventory.', 'kairoseth-ai-transparency' ); ?></p>121 <?php if ( empty( $results ) ) : ?> 122 <p><?php echo esc_html__( 'No recognized AI integrations were detected as active in the current WordPress plugin inventory.', 'kairoseth-ai-transparency' ); ?></p> 118 123 <?php else : ?> 119 <?php $this->render_result( $result, null !== $existing ); ?> 124 <div class="ai-transparency-discovery-results"> 125 <?php foreach ( $results as $result ) : ?> 126 <?php 127 $candidate = $result->supported() ? $result->candidate( gmdate( 'c' ) ) : null; 128 $existing = $candidate ? $registry->find( $candidate->id() ) : null; 129 $this->render_result( $result, null !== $existing ); 130 ?> 131 <?php endforeach; ?> 132 </div> 120 133 <?php endif; ?> 121 134 122 135 <p> 123 <a href="<?php echo esc_url( admin_url( ' tools.php?page=ai-transparency' ) ); ?>">136 <a href="<?php echo esc_url( admin_url( 'admin.php?page=ai-transparency' ) ); ?>"> 124 137 <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?> 125 138 </a> … … 136 149 public function handle_accept(): void { 137 150 $this->require_permission(); 138 check_admin_referer( 'ai_transparency_accept_ai_engine', '_kat_discovery_nonce' ); 139 140 $result = $this->detect_ai_engine(); 151 check_admin_referer( 'ai_transparency_accept_discovery', '_kat_discovery_nonce' ); 152 153 $detector_id = isset( $_POST['kat_detector_id'] ) ? sanitize_key( wp_unslash( $_POST['kat_detector_id'] ) ) : ''; 154 $signature = isset( $_POST['kat_source_signature'] ) ? sanitize_key( wp_unslash( $_POST['kat_source_signature'] ) ) : ''; 155 $result = $this->discovery_engine->find( 156 $this->inventory->observations(), 157 $detector_id, 158 $signature 159 ); 160 141 161 if ( null === $result ) { 142 162 $this->redirect_with_notice( 'not_found' ); … … 173 193 */ 174 194 private function render_result( DiscoveryResult $result, bool $already_registered ): void { 175 $evidence = $result->evidence(); 176 $plugin = isset( $evidence['plugin'] ) && is_array( $evidence['plugin'] ) ? $evidence['plugin'] : array(); 177 $status = $result->supported() ? __( 'Supported', 'kairoseth-ai-transparency' ) : __( 'Outside validated boundary', 'kairoseth-ai-transparency' ); 195 $evidence = $result->evidence(); 196 $plugin = isset( $evidence['plugin'] ) && is_array( $evidence['plugin'] ) ? $evidence['plugin'] : array(); 197 $supported_boundary = isset( $evidence['supported_version'] ) ? (string) $evidence['supported_version'] : '—'; 198 $status = $result->supported() ? __( 'Supported', 'kairoseth-ai-transparency' ) : __( 'Outside validated boundary', 'kairoseth-ai-transparency' ); 199 $section_id = sanitize_html_class( 'kat-discovery-' . $result->detector_id() . '-' . substr( $result->source_signature(), 0, 8 ) ); 178 200 ?> 179 <table class="widefat striped ai-transparency-discovery-evidence"> 180 <caption class="screen-reader-text"><?php echo esc_html__( 'Detected integration evidence', 'kairoseth-ai-transparency' ); ?></caption> 181 <tbody> 182 <tr> 183 <th scope="row"><?php echo esc_html__( 'Detected integration', 'kairoseth-ai-transparency' ); ?></th> 184 <td><?php echo esc_html( $result->integration_name() ); ?></td> 185 </tr> 186 <tr> 187 <th scope="row"><?php echo esc_html__( 'Observed version', 'kairoseth-ai-transparency' ); ?></th> 188 <td><?php echo esc_html( $result->observed_version() ); ?></td> 189 </tr> 190 <tr> 191 <th scope="row"><?php echo esc_html__( 'Supported detector boundary', 'kairoseth-ai-transparency' ); ?></th> 192 <td><?php echo esc_html( AiEngineDetector::SUPPORTED_VERSION ); ?></td> 193 </tr> 194 <tr> 195 <th scope="row"><?php echo esc_html__( 'Discovery status', 'kairoseth-ai-transparency' ); ?></th> 196 <td><?php echo esc_html( $status ); ?></td> 197 </tr> 198 <tr> 199 <th scope="row"><?php echo esc_html__( 'Plugin file', 'kairoseth-ai-transparency' ); ?></th> 200 <td><code><?php echo esc_html( isset( $plugin['plugin_file'] ) ? (string) $plugin['plugin_file'] : '' ); ?></code></td> 201 </tr> 202 <tr> 203 <th scope="row"><?php echo esc_html__( 'Text domain', 'kairoseth-ai-transparency' ); ?></th> 204 <td><code><?php echo esc_html( isset( $plugin['text_domain'] ) ? (string) $plugin['text_domain'] : '' ); ?></code></td> 205 </tr> 206 <tr> 207 <th scope="row"><?php echo esc_html__( 'Evidence signature', 'kairoseth-ai-transparency' ); ?></th> 208 <td><code><?php echo esc_html( $result->source_signature() ); ?></code></td> 209 </tr> 210 </tbody> 211 </table> 212 213 <?php if ( ! $result->supported() ) : ?> 214 <p><?php echo esc_html__( 'This AI Engine version is detected but is outside the currently validated detector boundary.', 'kairoseth-ai-transparency' ); ?></p> 215 <p><?php echo esc_html__( 'Review the installed version manually or wait for a validated detector update before adding it through discovery.', 'kairoseth-ai-transparency' ); ?></p> 216 <?php elseif ( $already_registered ) : ?> 217 <p><strong><?php echo esc_html__( 'Already in registry', 'kairoseth-ai-transparency' ); ?></strong></p> 218 <p><?php echo esc_html__( 'This detected integration has already been added to the local registry.', 'kairoseth-ai-transparency' ); ?></p> 219 <?php else : ?> 220 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 221 <input type="hidden" name="action" value="ai_transparency_accept_discovery"> 222 <?php wp_nonce_field( 'ai_transparency_accept_ai_engine', '_kat_discovery_nonce' ); ?> 223 <?php submit_button( __( 'Add to registry', 'kairoseth-ai-transparency' ), 'primary', 'submit', false ); ?> 224 </form> 225 <p><?php echo esc_html__( 'Adding the detected integration creates a local record with pending review status. You must still review its actual use and interaction context.', 'kairoseth-ai-transparency' ); ?></p> 226 <?php endif; ?> 201 <section class="ai-transparency-discovery-result" aria-labelledby="<?php echo esc_attr( $section_id ); ?>"> 202 <h3 id="<?php echo esc_attr( $section_id ); ?>"><?php echo esc_html( $result->integration_name() ); ?></h3> 203 <table class="widefat striped ai-transparency-discovery-evidence"> 204 <caption class="screen-reader-text"><?php echo esc_html__( 'Detected integration evidence', 'kairoseth-ai-transparency' ); ?></caption> 205 <tbody> 206 <tr> 207 <th scope="row"><?php echo esc_html__( 'Detected integration', 'kairoseth-ai-transparency' ); ?></th> 208 <td><?php echo esc_html( $result->integration_name() ); ?></td> 209 </tr> 210 <tr> 211 <th scope="row"><?php echo esc_html__( 'Observed version', 'kairoseth-ai-transparency' ); ?></th> 212 <td><?php echo esc_html( $result->observed_version() ); ?></td> 213 </tr> 214 <tr> 215 <th scope="row"><?php echo esc_html__( 'Supported detector boundary', 'kairoseth-ai-transparency' ); ?></th> 216 <td><?php echo esc_html( $supported_boundary ); ?></td> 217 </tr> 218 <tr> 219 <th scope="row"><?php echo esc_html__( 'Discovery status', 'kairoseth-ai-transparency' ); ?></th> 220 <td><?php echo esc_html( $status ); ?></td> 221 </tr> 222 <tr> 223 <th scope="row"><?php echo esc_html__( 'Plugin file', 'kairoseth-ai-transparency' ); ?></th> 224 <td><code><?php echo esc_html( isset( $plugin['plugin_file'] ) ? (string) $plugin['plugin_file'] : '' ); ?></code></td> 225 </tr> 226 <tr> 227 <th scope="row"><?php echo esc_html__( 'Text domain', 'kairoseth-ai-transparency' ); ?></th> 228 <td><code><?php echo esc_html( isset( $plugin['text_domain'] ) ? (string) $plugin['text_domain'] : '' ); ?></code></td> 229 </tr> 230 <tr> 231 <th scope="row"><?php echo esc_html__( 'Evidence signature', 'kairoseth-ai-transparency' ); ?></th> 232 <td><code><?php echo esc_html( $result->source_signature() ); ?></code></td> 233 </tr> 234 </tbody> 235 </table> 236 237 <?php if ( ! $result->supported() ) : ?> 238 <p><?php echo esc_html__( 'This integration version is detected but is outside its currently validated detector boundary.', 'kairoseth-ai-transparency' ); ?></p> 239 <p><?php echo esc_html__( 'Review the installed version manually or wait for a validated detector update before adding it through discovery.', 'kairoseth-ai-transparency' ); ?></p> 240 <?php elseif ( $already_registered ) : ?> 241 <p><strong><?php echo esc_html__( 'Already in registry', 'kairoseth-ai-transparency' ); ?></strong></p> 242 <p><?php echo esc_html__( 'This detected integration has already been added to the local registry.', 'kairoseth-ai-transparency' ); ?></p> 243 <?php else : ?> 244 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 245 <input type="hidden" name="action" value="ai_transparency_accept_discovery"> 246 <input type="hidden" name="kat_detector_id" value="<?php echo esc_attr( $result->detector_id() ); ?>"> 247 <input type="hidden" name="kat_source_signature" value="<?php echo esc_attr( $result->source_signature() ); ?>"> 248 <?php wp_nonce_field( 'ai_transparency_accept_discovery', '_kat_discovery_nonce' ); ?> 249 <?php submit_button( __( 'Add to registry', 'kairoseth-ai-transparency' ), 'primary', 'submit', false ); ?> 250 </form> 251 <p><?php echo esc_html__( 'Adding the detected integration creates a local record with pending review status. You must still review its actual use and interaction context.', 'kairoseth-ai-transparency' ); ?></p> 252 <?php endif; ?> 253 </section> 227 254 <?php 228 255 } 229 256 230 257 /** 231 * Re-run the AI Engine detector from the current WordPress inventory. 232 * 233 * @return DiscoveryResult|null 234 */ 235 private function detect_ai_engine() { 236 foreach ( $this->inventory->observations() as $observation ) { 237 $result = $this->ai_engine_detector->detect( $observation ); 238 if ( null !== $result ) { 239 return $result; 240 } 241 } 242 243 return null; 258 * Run all registered deterministic detectors against the current inventory. 259 * 260 * @return DiscoveryResult[] 261 */ 262 private function discover_results(): array { 263 return $this->discovery_engine->discover( $this->inventory->observations() ); 244 264 } 245 265 … … 255 275 'added' => __( 'Detected AI integration added to the registry for administrator review.', 'kairoseth-ai-transparency' ), 256 276 'exists' => __( 'The detected integration is already present in the registry.', 'kairoseth-ai-transparency' ), 257 'unsupported' => __( 'The AI Engine observation is outside thecurrently validated detector boundary.', 'kairoseth-ai-transparency' ),258 'not_found' => __( ' AI Engine could not be detected as an active plugin.', 'kairoseth-ai-transparency' ),277 'unsupported' => __( 'The selected integration observation is outside its currently validated detector boundary.', 'kairoseth-ai-transparency' ), 278 'not_found' => __( 'The selected AI integration could not be matched to the current active plugin inventory.', 'kairoseth-ai-transparency' ), 259 279 'save_failed' => __( 'The AI systems registry could not be saved.', 'kairoseth-ai-transparency' ), 260 280 ); … … 264 284 } 265 285 266 $class = 'save_failed' === $notice? 'notice notice-error' : 'notice notice-success';286 $class = in_array( $notice, array( 'save_failed', 'unsupported', 'not_found' ), true ) ? 'notice notice-error' : 'notice notice-success'; 267 287 ?> 268 <div class="<?php echo esc_attr( $class ); ?>" role="<?php echo esc_attr( ' save_failed' === $notice? 'alert' : 'status' ); ?>">288 <div class="<?php echo esc_attr( $class ); ?>" role="<?php echo esc_attr( 'notice notice-error' === $class ? 'alert' : 'status' ); ?>"> 269 289 <p><?php echo esc_html( $messages[ $notice ] ); ?></p> 270 290 </div> … … 295 315 'kat_discovery_notice' => sanitize_key( $notice ), 296 316 ), 297 admin_url( ' tools.php' )317 admin_url( 'admin.php' ) 298 318 ); 299 319 -
kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-evidenceexportpage.php
r3735383 r3738042 124 124 125 125 <p> 126 <a href="<?php echo esc_url( admin_url( ' tools.php?page=ai-transparency' ) ); ?>">126 <a href="<?php echo esc_url( admin_url( 'admin.php?page=ai-transparency' ) ); ?>"> 127 127 <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?> 128 128 </a> -
kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-readinesspage.php
r3735383 r3738042 109 109 110 110 <p> 111 <a href="<?php echo esc_url( admin_url( ' tools.php?page=ai-transparency' ) ); ?>">111 <a href="<?php echo esc_url( admin_url( 'admin.php?page=ai-transparency' ) ); ?>"> 112 112 <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?> 113 113 </a> … … 172 172 case Finding::FACT_CONTEXT_MISSING: 173 173 return __( 'No interaction context is recorded for this active AI system.', 'kairoseth-ai-transparency' ); 174 case Finding::FACT_ADMIN_CONTEXT_INCOMPLETE: 175 return __( 'One or more administrator context fields are not documented for this active AI system.', 'kairoseth-ai-transparency' ); 174 176 case Finding::FACT_SYSTEM_ACTIVE: 175 177 return __( 'This AI system is active in the local AI Systems Registry.', 'kairoseth-ai-transparency' ); … … 205 207 case Finding::GUIDANCE_DOCUMENT_CONTEXT: 206 208 return __( 'Document where or how visitors, staff or customers interact with this AI system.', 'kairoseth-ai-transparency' ); 209 case Finding::GUIDANCE_DOCUMENT_ADMIN_CONTEXT: 210 return __( 'Review the missing administrator context fields and document those that are applicable to this system.', 'kairoseth-ai-transparency' ); 207 211 case Finding::GUIDANCE_VERIFY_DISCLOSURE: 208 212 return __( 'Verify that the configured disclosure has an appropriate implementation and placement for the actual workflow.', 'kairoseth-ai-transparency' ); -
kairoseth-ai-transparency/tags/1.2.0/src/Discovery/class-aienginedetector.php
r3735383 r3738042 11 11 * Detects a validated AI Engine installation from WordPress plugin inventory evidence. 12 12 */ 13 final class AiEngineDetector {13 final class AiEngineDetector implements DetectorInterface { 14 14 public const DETECTOR_ID = 'ai-engine-plugin'; 15 15 public const INTEGRATION_SLUG = 'ai-engine'; -
kairoseth-ai-transparency/tags/1.2.0/src/Domain/class-aisystem.php
r3735383 r3738042 95 95 96 96 /** 97 * Administrator-declared business or operational purpose. 98 * 99 * @var string 100 */ 101 private $purpose; 102 103 /** 104 * Administrator-declared provider or vendor. 105 * 106 * @var string 107 */ 108 private $provider; 109 110 /** 111 * Administrator-declared model or model family. 112 * 113 * @var string 114 */ 115 private $model; 116 117 /** 118 * Administrator-declared audience or affected user group. 119 * 120 * @var string 121 */ 122 private $audience; 123 124 /** 125 * Administrator-declared human oversight process. 126 * 127 * @var string 128 */ 129 private $human_oversight; 130 131 /** 132 * Administrator-declared data-use context. 133 * 134 * @var string 135 */ 136 private $data_use_context; 137 138 /** 97 139 * Creation timestamp. 98 140 * … … 123 165 * @param string $source Evidence/source identifier. 124 166 * @param bool $interaction_disclosure_required Whether interaction disclosure is required by the configured workflow. 125 * @param array<string, mixed> $metadata Optional lifecycle and originmetadata.167 * @param array<string, mixed> $metadata Optional lifecycle, origin and administrator-authored context metadata. 126 168 * @throws InvalidArgumentException When the record is invalid. 127 169 */ … … 172 214 $this->review_status = $review_status; 173 215 $this->interaction_context = isset( $metadata['interaction_context'] ) ? trim( (string) $metadata['interaction_context'] ) : ''; 216 $this->purpose = isset( $metadata['purpose'] ) ? trim( (string) $metadata['purpose'] ) : ''; 217 $this->provider = isset( $metadata['provider'] ) ? trim( (string) $metadata['provider'] ) : ''; 218 $this->model = isset( $metadata['model'] ) ? trim( (string) $metadata['model'] ) : ''; 219 $this->audience = isset( $metadata['audience'] ) ? trim( (string) $metadata['audience'] ) : ''; 220 $this->human_oversight = isset( $metadata['human_oversight'] ) ? trim( (string) $metadata['human_oversight'] ) : ''; 221 $this->data_use_context = isset( $metadata['data_use_context'] ) ? trim( (string) $metadata['data_use_context'] ) : ''; 174 222 $this->created_at = isset( $metadata['created_at'] ) ? trim( (string) $metadata['created_at'] ) : ''; 175 223 $this->updated_at = isset( $metadata['updated_at'] ) ? trim( (string) $metadata['updated_at'] ) : ''; … … 195 243 'review_status' => $data['review_status'] ?? self::REVIEW_PENDING, 196 244 'interaction_context' => $data['interaction_context'] ?? '', 245 'purpose' => $data['purpose'] ?? '', 246 'provider' => $data['provider'] ?? '', 247 'model' => $data['model'] ?? '', 248 'audience' => $data['audience'] ?? '', 249 'human_oversight' => $data['human_oversight'] ?? '', 250 'data_use_context' => $data['data_use_context'] ?? '', 197 251 'created_at' => $data['created_at'] ?? '', 198 252 'updated_at' => $data['updated_at'] ?? '', … … 291 345 public function interaction_context(): string { 292 346 return $this->interaction_context; 347 } 348 349 /** 350 * Get the administrator-declared purpose. 351 * 352 * @return string 353 */ 354 public function purpose(): string { 355 return $this->purpose; 356 } 357 358 /** 359 * Get the administrator-declared provider. 360 * 361 * @return string 362 */ 363 public function provider(): string { 364 return $this->provider; 365 } 366 367 /** 368 * Get the administrator-declared model or model family. 369 * 370 * @return string 371 */ 372 public function model(): string { 373 return $this->model; 374 } 375 376 /** 377 * Get the administrator-declared audience. 378 * 379 * @return string 380 */ 381 public function audience(): string { 382 return $this->audience; 383 } 384 385 /** 386 * Get the administrator-declared human oversight process. 387 * 388 * @return string 389 */ 390 public function human_oversight(): string { 391 return $this->human_oversight; 392 } 393 394 /** 395 * Get the administrator-declared data-use context. 396 * 397 * @return string 398 */ 399 public function data_use_context(): string { 400 return $this->data_use_context; 293 401 } 294 402 … … 335 443 'review_status' => $this->review_status, 336 444 'interaction_context' => $this->interaction_context, 445 'purpose' => $this->purpose, 446 'provider' => $this->provider, 447 'model' => $this->model, 448 'audience' => $this->audience, 449 'human_oversight' => $this->human_oversight, 450 'data_use_context' => $this->data_use_context, 337 451 'interaction_disclosure_required' => $this->interaction_disclosure_required, 338 452 'created_at' => $this->created_at, -
kairoseth-ai-transparency/tags/1.2.0/src/Evidence/class-finding.php
r3735383 r3738042 17 17 public const PRIORITY_INFO = 'info'; 18 18 19 public const FACT_REVIEW_PENDING = 'review_pending'; 20 public const FACT_CONTEXT_MISSING = 'interaction_context_missing'; 21 public const FACT_SYSTEM_ACTIVE = 'system_active'; 19 public const FACT_REVIEW_PENDING = 'review_pending'; 20 public const FACT_CONTEXT_MISSING = 'interaction_context_missing'; 21 public const FACT_ADMIN_CONTEXT_INCOMPLETE = 'administrator_context_incomplete'; 22 public const FACT_SYSTEM_ACTIVE = 'system_active'; 22 23 23 24 public const DECLARATION_NONE = ''; 24 25 public const DECLARATION_DISCLOSURE_REQUIRED = 'interaction_disclosure_required'; 25 26 26 public const GUIDANCE_COMPLETE_REVIEW = 'complete_system_review'; 27 public const GUIDANCE_DOCUMENT_CONTEXT = 'document_interaction_context'; 28 public const GUIDANCE_VERIFY_DISCLOSURE = 'verify_disclosure_implementation'; 27 public const GUIDANCE_COMPLETE_REVIEW = 'complete_system_review'; 28 public const GUIDANCE_DOCUMENT_CONTEXT = 'document_interaction_context'; 29 public const GUIDANCE_DOCUMENT_ADMIN_CONTEXT = 'document_administrator_context'; 30 public const GUIDANCE_VERIFY_DISCLOSURE = 'verify_disclosure_implementation'; 29 31 30 32 /** -
kairoseth-ai-transparency/tags/1.2.0/src/Evidence/class-findingengine.php
r3735383 r3738042 15 15 */ 16 16 final class FindingEngine { 17 public const RULE_REVIEW_PENDING = 'registry_review_pending_v1'; 18 public const RULE_CONTEXT_MISSING = 'interaction_context_missing_v1'; 19 public const RULE_DISCLOSURE_REVIEW = 'configured_disclosure_review_v1'; 17 public const RULE_REVIEW_PENDING = 'registry_review_pending_v1'; 18 public const RULE_CONTEXT_MISSING = 'interaction_context_missing_v1'; 19 public const RULE_ADMIN_CONTEXT_INCOMPLETE = 'administrator_context_incomplete_v1'; 20 public const RULE_DISCLOSURE_REVIEW = 'configured_disclosure_review_v1'; 20 21 21 22 /** … … 68 69 } 69 70 71 $missing_admin_context = $this->missing_admin_context( $system ); 72 if ( AiSystem::REVIEW_REVIEWED === $system->review_status() && ! empty( $missing_admin_context ) ) { 73 $findings[] = $this->build_finding( 74 self::RULE_ADMIN_CONTEXT_INCOMPLETE, 75 'registry_completeness', 76 Finding::PRIORITY_REVIEW, 77 $system, 78 Finding::FACT_ADMIN_CONTEXT_INCOMPLETE, 79 Finding::DECLARATION_NONE, 80 Finding::GUIDANCE_DOCUMENT_ADMIN_CONTEXT, 81 array( 82 'missing_fields' => $missing_admin_context, 83 'status' => $system->status(), 84 ), 85 $generated_at 86 ); 87 } 88 70 89 if ( $system->interaction_disclosure_required() ) { 71 90 $findings[] = $this->build_finding( … … 94 113 95 114 return $findings; 115 } 116 117 /** 118 * Return administrator-authored context fields that remain undocumented. 119 * 120 * @param AiSystem $system AI system record. 121 * @return string[] 122 */ 123 private function missing_admin_context( AiSystem $system ): array { 124 $context = array( 125 'purpose' => $system->purpose(), 126 'provider' => $system->provider(), 127 'model' => $system->model(), 128 'audience' => $system->audience(), 129 'human_oversight' => $system->human_oversight(), 130 'data_use_context' => $system->data_use_context(), 131 ); 132 $missing = array(); 133 134 foreach ( $context as $field => $value ) { 135 if ( '' === $value ) { 136 $missing[] = $field; 137 } 138 } 139 140 return $missing; 96 141 } 97 142 -
kairoseth-ai-transparency/tags/1.2.0/src/Registry/class-registryschema.php
r3735383 r3738042 15 15 */ 16 16 final class RegistrySchema { 17 public const VERSION = 1;17 public const VERSION = 2; 18 18 19 19 /** … … 67 67 * 68 68 * Version 0 accepted either a raw list of records or an array with a systems key. 69 * Missing Phase 2 metadata is filled by AiSystem defaults during hydration. 69 * Version 1 introduced lifecycle and review metadata. Version 2 adds optional 70 * administrator-authored system context. Missing fields are always normalized 71 * to empty strings rather than inferred from detector evidence. 70 72 * 71 73 * @param mixed $payload Persisted value. … … 105 107 'review_status' => isset( $system_data['review_status'] ) ? (string) $system_data['review_status'] : AiSystem::REVIEW_PENDING, 106 108 'interaction_context' => isset( $system_data['interaction_context'] ) ? (string) $system_data['interaction_context'] : '', 109 'purpose' => isset( $system_data['purpose'] ) ? (string) $system_data['purpose'] : '', 110 'provider' => isset( $system_data['provider'] ) ? (string) $system_data['provider'] : '', 111 'model' => isset( $system_data['model'] ) ? (string) $system_data['model'] : '', 112 'audience' => isset( $system_data['audience'] ) ? (string) $system_data['audience'] : '', 113 'human_oversight' => isset( $system_data['human_oversight'] ) ? (string) $system_data['human_oversight'] : '', 114 'data_use_context' => isset( $system_data['data_use_context'] ) ? (string) $system_data['data_use_context'] : '', 107 115 'interaction_disclosure_required' => ! empty( $system_data['interaction_disclosure_required'] ), 108 116 'created_at' => isset( $system_data['created_at'] ) ? (string) $system_data['created_at'] : '', -
kairoseth-ai-transparency/tags/1.2.0/src/class-plugin.php
r3735383 r3738042 8 8 namespace Kairoseth\AITransparency; 9 9 10 use Kairoseth\AITransparency\Admin\AdminNavigation; 10 11 use Kairoseth\AITransparency\Admin\AdminPage; 12 use Kairoseth\AITransparency\Admin\DashboardPage; 11 13 use Kairoseth\AITransparency\Admin\DisclosurePage; 12 14 use Kairoseth\AITransparency\Admin\DiscoveryPage; … … 66 68 67 69 if ( is_admin() ) { 68 ( new AdminPage() )->register(); 69 ( new DiscoveryPage() )->register(); 70 ( new ReadinessPage() )->register(); 71 ( new DisclosurePage() )->register(); 72 ( new EvidenceExportPage() )->register(); 73 ( new SupportPage() )->register(); 70 $dashboard_page = new DashboardPage(); 71 $registry_page = new AdminPage(); 72 $discovery_page = new DiscoveryPage(); 73 $readiness_page = new ReadinessPage(); 74 $disclosure_page = new DisclosurePage(); 75 $evidence_export_page = new EvidenceExportPage(); 76 $support_page = new SupportPage(); 77 78 $dashboard_page->register(); 79 $registry_page->register(); 80 $discovery_page->register(); 81 $readiness_page->register(); 82 $disclosure_page->register(); 83 $evidence_export_page->register(); 84 $support_page->register(); 85 86 ( new AdminNavigation( 87 $registry_page, 88 $discovery_page, 89 $readiness_page, 90 $disclosure_page, 91 $evidence_export_page, 92 $support_page 93 ) )->register(); 74 94 } 75 95 } -
kairoseth-ai-transparency/trunk/ai-transparency.php
r3735383 r3738042 4 4 * Plugin URI: https://kairoseth.com/products/ai-transparency 5 5 * Description: EU AI Act transparency readiness tooling for WordPress, with local AI system inventory, evidence and disclosure workflows. 6 * Version: 1. 0.36 * Version: 1.2.0 7 7 * Requires at least: 6.6 8 8 * Requires PHP: 7.4 … … 20 20 } 21 21 22 define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1. 0.3' );22 define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1.2.0' ); 23 23 define( 'KAIROSETH_AI_TRANSPARENCY_FILE', __FILE__ ); 24 24 define( 'KAIROSETH_AI_TRANSPARENCY_PATH', plugin_dir_path( __FILE__ ) ); -
kairoseth-ai-transparency/trunk/assets/admin.css
r3735383 r3738042 123 123 max-width: 760px; 124 124 font-style: italic; 125 } 126 127 .kat-dashboard { 128 max-width: 1280px; 129 } 130 131 .kat-dashboard-hero { 132 display: flex; 133 align-items: center; 134 justify-content: space-between; 135 gap: 24px; 136 margin: 20px 0 24px; 137 padding: 30px; 138 border: 1px solid #184fb1; 139 border-radius: 16px; 140 background: linear-gradient(135deg, #081a45 0%, #0b2c68 48%, #123b8d 100%); 141 box-shadow: 0 12px 32px rgba(8, 26, 69, 0.16); 142 color: #fff; 143 } 144 145 .kat-dashboard-hero h1 { 146 margin: 4px 0 8px; 147 color: #fff; 148 font-size: clamp(28px, 4vw, 42px); 149 line-height: 1.08; 150 } 151 152 .kat-dashboard-hero p { 153 max-width: 760px; 154 margin: 0; 155 color: #d9e8ff; 156 font-size: 15px; 157 } 158 159 .kat-dashboard-kicker { 160 font-size: 12px !important; 161 font-weight: 700; 162 letter-spacing: .12em; 163 text-transform: uppercase; 164 color: #66e4ff !important; 165 } 166 167 .kat-dashboard-version { 168 min-width: 130px; 169 padding: 14px 16px; 170 border: 1px solid rgba(255, 255, 255, .18); 171 border-radius: 12px; 172 background: rgba(255, 255, 255, .08); 173 text-align: center; 174 } 175 176 .kat-dashboard-version span, 177 .kat-dashboard-version strong { 178 display: block; 179 } 180 181 .kat-dashboard-version span { 182 margin-bottom: 4px; 183 font-size: 12px; 184 color: #bcd5ff; 185 } 186 187 .kat-dashboard-version strong { 188 font-size: 22px; 189 color: #fff; 190 } 191 192 .kat-dashboard-metrics { 193 display: grid; 194 grid-template-columns: repeat(4, minmax(0, 1fr)); 195 gap: 16px; 196 margin-bottom: 20px; 197 } 198 199 .kat-dashboard-metric { 200 display: flex; 201 align-items: center; 202 gap: 14px; 203 min-width: 0; 204 padding: 18px; 205 border: 1px solid #d8e3f5; 206 border-radius: 12px; 207 background: #fff; 208 box-shadow: 0 4px 14px rgba(17, 47, 95, .06); 209 } 210 211 .kat-dashboard-metric > .dashicons { 212 width: 42px; 213 height: 42px; 214 border-radius: 10px; 215 background: #eaf5ff; 216 color: #0f72cf; 217 font-size: 23px; 218 line-height: 42px; 219 text-align: center; 220 } 221 222 .kat-dashboard-metric strong, 223 .kat-dashboard-metric span { 224 display: block; 225 } 226 227 .kat-dashboard-metric strong { 228 font-size: 26px; 229 line-height: 1; 230 color: #10213f; 231 } 232 233 .kat-dashboard-metric div > span { 234 margin-top: 5px; 235 color: #52637d; 236 } 237 238 .kat-dashboard-status { 239 display: flex; 240 align-items: center; 241 justify-content: space-between; 242 gap: 24px; 243 margin: 0 0 28px; 244 padding: 20px 22px; 245 border: 1px solid #cfe6ef; 246 border-left: 4px solid #16a6c9; 247 border-radius: 10px; 248 background: #f8fdff; 249 } 250 251 .kat-dashboard-status h2 { 252 margin: 0 0 8px; 253 font-size: 18px; 254 } 255 256 .kat-dashboard-status-list { 257 margin: 0; 258 padding-left: 20px; 259 } 260 261 .kat-dashboard-status-list li { 262 margin: 4px 0; 263 } 264 265 .kat-dashboard-section-title { 266 margin: 0 0 14px; 267 font-size: 20px; 268 } 269 270 .kat-dashboard-grid { 271 display: grid; 272 grid-template-columns: repeat(3, minmax(0, 1fr)); 273 gap: 16px; 274 } 275 276 .kat-dashboard-card { 277 display: flex; 278 flex-direction: column; 279 min-width: 0; 280 min-height: 210px; 281 padding: 22px; 282 border: 1px solid #d7e2f1; 283 border-radius: 14px; 284 background: linear-gradient(180deg, #fff 0%, #f8fbff 100%); 285 box-shadow: 0 5px 18px rgba(17, 47, 95, .06); 286 } 287 288 .kat-dashboard-card-icon { 289 display: inline-flex; 290 align-items: center; 291 justify-content: center; 292 width: 44px; 293 height: 44px; 294 margin-bottom: 14px; 295 border-radius: 11px; 296 background: linear-gradient(135deg, #e2f9ff, #eef0ff); 297 color: #176fd1; 298 } 299 300 .kat-dashboard-card-icon .dashicons { 301 width: 24px; 302 height: 24px; 303 font-size: 24px; 304 } 305 306 .kat-dashboard-card h3 { 307 margin: 0 0 8px; 308 font-size: 17px; 309 color: #10213f; 310 } 311 312 .kat-dashboard-card p { 313 flex: 1; 314 margin: 0 0 18px; 315 color: #52637d; 316 line-height: 1.55; 317 } 318 319 .kat-dashboard-card-link { 320 align-self: flex-start; 321 font-weight: 600; 322 text-decoration: none; 323 } 324 325 .kat-dashboard-card-link:focus-visible { 326 outline: 2px solid currentColor; 327 outline-offset: 3px; 328 } 329 330 .kat-dashboard-boundary { 331 max-width: 920px; 332 margin: 24px 0 0; 333 color: #59677a; 334 } 335 336 @media screen and (max-width: 1100px) { 337 .kat-dashboard-metrics { 338 grid-template-columns: repeat(2, minmax(0, 1fr)); 339 } 340 341 .kat-dashboard-grid { 342 grid-template-columns: repeat(2, minmax(0, 1fr)); 343 } 125 344 } 126 345 … … 175 394 min-height: 44px; 176 395 } 177 } 396 397 .kat-dashboard-hero, 398 .kat-dashboard-status { 399 align-items: flex-start; 400 flex-direction: column; 401 } 402 403 .kat-dashboard-version { 404 text-align: left; 405 } 406 407 .kat-dashboard-metrics, 408 .kat-dashboard-grid { 409 grid-template-columns: minmax(0, 1fr); 410 } 411 412 .kat-dashboard-card { 413 min-height: 0; 414 } 415 } -
kairoseth-ai-transparency/trunk/readme.txt
r3735383 r3738042 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1. 0.37 Stable tag: 1.2.0 8 8 License: MIT 9 9 License URI: https://opensource.org/license/mit/ … … 17 17 The stable release includes: 18 18 19 * a local AI Systems Registry under **Tools > AI Transparency**; 20 * deterministic AI integration discovery under **Tools > AI Discovery**; 21 * deterministic technical readiness findings under **Tools > AI Readiness**; 22 * explicit administrator-controlled disclosure readiness under **Tools > AI Disclosure**; 23 * privileged local JSON evidence export under **Tools > AI Evidence Export**; 24 * an optional, administrator-initiated Kairoseth support bridge under **Tools > AI Transparency Support**. 25 26 An authorized administrator can add, edit, review and archive AI system records. Registry data is stored locally in the current WordPress site's Options storage with a versioned schema. 27 28 The first validated discovery detector supports **AI Engine 3.7.7** from bounded WordPress plugin identity evidence. Discovery does not inspect AI provider credentials, provider/model settings, prompts, conversations or AI Engine internal configuration. Supported discovery results require explicit administrator acceptance and enter the Registry as pending review. 19 * a dedicated **Kairoseth AI Transparency** admin dashboard with live Registry, review, readiness and disclosure-ready metrics; 20 * a local AI Systems Registry under **Kairoseth AI Transparency > AI Systems Registry**, including instant local search and filters by type, review and lifecycle status; 21 * administrator-authored context for purpose, provider, model/model family, audience, human oversight and data-use context; 22 * deterministic AI integration discovery under **Kairoseth AI Transparency > AI Discovery**; 23 * deterministic technical readiness findings under **Kairoseth AI Transparency > AI Readiness**; 24 * explicit administrator-controlled disclosure readiness under **Kairoseth AI Transparency > AI Disclosure**; 25 * privileged local JSON evidence export under **Kairoseth AI Transparency > AI Evidence Export**; 26 * an optional, administrator-initiated Kairoseth support bridge under **Kairoseth AI Transparency > AI Transparency Support**. 27 28 Legacy Tools URLs from the 1.0.x line remain registered for backwards compatibility, while the visible workflow is grouped under the dedicated Kairoseth AI Transparency menu. 29 30 An authorized administrator can add, edit, review and archive AI system records. Registry data is stored locally in the current WordPress site's Options storage with a versioned schema. Version 1.2 upgrades Registry persistence to schema v2 and migrates earlier records without inventing provider, model, purpose or other administrator declarations. 31 32 AI Discovery uses an extensible deterministic detector architecture. The validated 1.2 detectors support: 33 34 * **AI Engine 3.7.7**; 35 * **AI Puffer 2.4.97** (`gpt3-ai-content-generator/gpt3-ai-content-generator.php`). 36 37 Discovery evaluates bounded WordPress plugin identity, version and activation-state evidence. It does not inspect AI provider credentials, prompts, conversations or private provider configuration, and it does not infer purpose, provider, model, audience, oversight or data-use declarations. Supported discovery results require explicit administrator acceptance and enter the Registry as pending review. 29 38 30 39 AI Readiness generates findings on demand from the current Registry and keeps three concepts separate: … … 33 42 * **Administrator declaration** — an explicit administrator state when relevant; 34 43 * **Guidance** — a technical review or completion action. 44 45 Version 1.2 can also flag a reviewed active record when one or more administrator-context fields remain undocumented. This is a bounded completeness signal, not a legal-compliance decision. 35 46 36 47 AI Disclosure renders only from server-authoritative reviewed Registry state. The public shortcode is: … … 48 59 * a deterministic SHA-256 `snapshot_signature` that excludes generation time; 49 60 * unchanged technical state produces the same signature across export times; 50 * meaningful exported Registry changes change the signature;61 * meaningful exported Registry changes, including administrator-authored context changes, change the signature; 51 62 * archived Registry records are retained; 52 63 * an empty Registry is valid; … … 55 66 * no arbitrary WordPress/plugin option dump or automatic export of credentials, tokens, cookies, nonces, user identities, prompts, conversations, logs or database dumps. 56 67 57 The privileged export may contain administrator-authored `interaction_context`, so the file may contain confidential operational contextand should be handled accordingly.68 The privileged export may contain administrator-authored operational context from the Registry, including interaction context and the 1.2 context fields, so the file may contain confidential business information and should be handled accordingly. 58 69 59 70 **Important:** This plugin provides technical readiness, workflow and evidence tooling. It does not certify or guarantee compliance with the EU AI Act or any other law. The evidence `snapshot_signature` is a technical snapshot identity, not a legal/digital signature, trusted timestamp, non-repudiation proof or regulatory certification. … … 65 76 The plugin is local-first. Registry, Discovery, Readiness, Disclosure and Evidence Export do not require Kairoseth or automatically send their state to an external service. 66 77 67 **Kairoseth Custom Requests** is an optional support/custom-integration service used only after an administrator deliberately clicks a link on ** Tools> AI Transparency Support**. Loading the WordPress support page itself makes no Kairoseth request.78 **Kairoseth Custom Requests** is an optional support/custom-integration service used only after an administrator deliberately clicks a link on **Kairoseth AI Transparency > AI Transparency Support**. Loading the WordPress support page itself makes no Kairoseth request. 68 79 69 80 The explicit browser navigation goes to `https://kairoseth.com/custom-requests` and includes only this bounded technical/product context in the URL: … … 78 89 * the administrator-selected bounded request type. 79 90 80 The plugin does **not** automatically attach or transmit the site URL, administrator identity, Registry contents, AI system names, interaction context, Discovery evidence, Readiness findings, Disclosure state, Evidence Export JSON/signature, plugin/theme inventory, server paths, credentials, prompts, conversations, logs or database contents.91 The plugin does **not** automatically attach or transmit the site URL, administrator identity, Registry contents, AI system names, interaction context, administrator context fields, Discovery evidence, Readiness findings, Disclosure state, Evidence Export JSON/signature, plugin/theme inventory, server paths, credentials, prompts, conversations, logs or database contents. 81 92 82 93 After reaching Kairoseth, the administrator decides what contact, business or request information to enter and submit. Kairoseth owns the request form, its consent flow and final submission. … … 90 101 1. Upload the plugin directory to `/wp-content/plugins/` or install the packaged ZIP. 91 102 2. Activate **Kairoseth AI Transparency** from the Plugins screen. 92 3. Open **Tools > AI Transparency** to maintain the local Registry. 93 4. Open **Tools > AI Discovery** to review supported deterministic integration evidence. 94 5. Open **Tools > AI Readiness** to review current technical findings. 95 6. Open **Tools > AI Disclosure** to review disclosure readiness and copy a shortcode for an eligible system. 96 7. Open **Tools > AI Evidence Export** to download the current site-local JSON evidence snapshot. 97 8. Optionally open **Tools > AI Transparency Support** for an explicit privacy-bounded handoff to Kairoseth support/custom requests. 103 3. Open **Kairoseth AI Transparency** for the overview dashboard and current technical status. 104 4. Open **Kairoseth AI Transparency > AI Systems Registry** to maintain, search and filter the local Registry. 105 5. Open **Kairoseth AI Transparency > AI Discovery** to review supported deterministic integration evidence. 106 6. Open **Kairoseth AI Transparency > AI Readiness** to review current technical findings. 107 7. Open **Kairoseth AI Transparency > AI Disclosure** to review disclosure readiness and copy a shortcode for an eligible system. 108 8. Open **Kairoseth AI Transparency > AI Evidence Export** to download the current site-local JSON evidence snapshot. 109 9. Optionally open **Kairoseth AI Transparency > AI Transparency Support** for an explicit privacy-bounded handoff to Kairoseth support/custom requests. 98 110 99 111 == Frequently Asked Questions == … … 109 121 = What does AI Discovery detect? = 110 122 111 The first validated detector recognizes an active AI Engine 3.7.7 installation from WordPress plugin inventory evidence. It does not infer which AI provider, model, chatbot or workflow is actually configured. 123 Version 1.2 includes validated deterministic detectors for AI Engine 3.7.7 and AI Puffer 2.4.97. Discovery identifies only supported plugin identity/version/activation evidence and does not infer which provider, model, purpose, audience or workflow is actually configured. 124 125 = What information can I add to the Registry in 1.2? = 126 127 In addition to the existing technical record, an administrator can document purpose, provider, model/model family, audience, human oversight and data-use context. These values remain explicit administrator declarations and are not filled automatically by Discovery. 112 128 113 129 = What does AI Readiness report? = 114 130 115 The readiness engine reports reproducible technical review conditions derived from the local AI Systems Registry. It keeps observed facts, administrator declarations and technical guidance separate anddoes not make legal compliance decisions.131 The readiness engine reports reproducible technical review conditions derived from the local AI Systems Registry. It keeps observed facts, administrator declarations and technical guidance separate. In 1.2 it can also highlight reviewed active records with incomplete administrator context. It does not make legal compliance decisions. 116 132 117 133 = What is the Evidence Export snapshot signature? = 118 134 119 It is a SHA-256 identity of the bounded stable technical snapshot. Generation time is excluded so repeated exports of unchanged technical state keep the same signature. It is not a legal/digital signature or external timestamp.135 It is a SHA-256 identity of the bounded stable technical snapshot. Generation time is excluded so repeated exports of unchanged technical state keep the same signature. Meaningful Registry changes, including administrator-context changes, change the signature. It is not a legal/digital signature or external timestamp. 120 136 121 137 = Is Evidence Export stored or uploaded? = … … 123 139 No. Evidence Export v1 is built for the explicit administrator request and returned as a direct JSON attachment. The plugin does not persist the generated file or upload it to Kairoseth. 124 140 141 = What happens to my Registry if I update from an earlier version? = 142 143 Version 1.2 migrates earlier Registry records to schema v2 on first access while preserving existing lifecycle, review, disclosure and interaction-context state. New administrator-context fields begin empty unless an administrator explicitly documents them. 144 125 145 = What happens to my Registry if I deactivate or remove the plugin? = 126 146 … … 129 149 = How is Multisite handled? = 130 150 131 Registry and Evidence Export follow the authoritative current blog/site context. The firstexport does not aggregate the full network. A network uninstall removes only the plugin-owned Registry option from each existing site.151 Registry and Evidence Export follow the authoritative current blog/site context. The export does not aggregate the full network. A network uninstall removes only the plugin-owned Registry option from each existing site. 132 152 133 153 = Does it detect whether any article was written by AI? = … … 136 156 137 157 == Changelog == 158 159 = 1.2.0 = 160 * Adds an extensible multi-detector Discovery engine while preserving server-authoritative, deterministic evidence selection. 161 * Adds validated deterministic Discovery support for AI Puffer 2.4.97 alongside AI Engine 3.7.7. 162 * Upgrades the site-local Registry to schema v2 with administrator-authored purpose, provider, model/model family, audience, human oversight and data-use context. 163 * Migrates earlier Registry records safely with the new declaration fields empty instead of inferring values from plugin identity or Discovery evidence. 164 * Adds Registry form controls for all six administrator-context fields with explicit local-first and declaration boundaries. 165 * Extends deterministic Evidence Export snapshots so administrator-context changes participate in the stable snapshot identity. 166 * Adds a bounded Readiness finding for reviewed active systems whose administrator context remains incomplete. 167 * Preserves Fact / Administrator declaration / Guidance separation and does not convert completeness findings into legal-compliance claims. 168 * Extends real WordPress, Multisite, accessibility, migration, exact-ZIP lifecycle and EN/ES validation for the 1.2 data model. 169 170 = 1.1.0 = 171 * Adds a dedicated top-level Kairoseth AI Transparency dashboard with live Registry, review, readiness and disclosure-ready metrics. 172 * Groups Registry, Discovery, Readiness, Disclosure, Evidence Export and Support under one visible plugin menu while preserving legacy 1.0.x Tools URLs for backwards compatibility. 173 * Adds a recent Registry activity table with direct edit actions from the dashboard. 174 * Adds instant local Registry search and filters by system type, review status and lifecycle status without changing the persistence schema. 175 * Improves admin cards, tables, forms, findings, responsive behavior, keyboard focus handling and reduced-motion behavior. 176 * Aligns Spanish source-catalog metadata and the translation template with version 1.1.0 and the canonical `kairoseth-ai-transparency` text domain. 177 * Keeps Registry storage, Evidence Export schema and existing 1.0.x site-local data compatible with the 1.0.x line. 138 178 139 179 = 1.0.3 = … … 170 210 == Upgrade Notice == 171 211 212 = 1.2.0 = 213 Adds multi-detector Discovery, AI Puffer support and Registry schema v2 with explicit administrator context. Existing Registry records are preserved and migrated safely with new declaration fields empty until reviewed. 214 215 = 1.1.0 = 216 Adds the unified Kairoseth AI Transparency dashboard, admin navigation and Registry search/filter UX while preserving the existing site-local Registry schema and 1.0.x data. 217 172 218 = 1.0.3 = 173 219 Ownership-metadata clarification for the ongoing WordPress.org review. No runtime behavior or stored-data schema changes. -
kairoseth-ai-transparency/trunk/src/Admin/class-adminpage.php
r3735383 r3738042 125 125 'system' => $system->id(), 126 126 ), 127 admin_url( ' tools.php' )127 admin_url( 'admin.php' ) 128 128 ); 129 129 ?> … … 172 172 check_admin_referer( 'kairoseth_ai_transparency_save_system', '_kat_nonce' ); 173 173 174 $registry = $this->repository->load(); 175 $id = isset( $_POST['system_id'] ) ? sanitize_text_field( wp_unslash( $_POST['system_id'] ) ) : ''; 176 $name = isset( $_POST['system_name'] ) ? sanitize_text_field( wp_unslash( $_POST['system_name'] ) ) : ''; 177 $type = isset( $_POST['system_type'] ) ? sanitize_key( wp_unslash( $_POST['system_type'] ) ) : ''; 178 $context = isset( $_POST['interaction_context'] ) ? sanitize_textarea_field( wp_unslash( $_POST['interaction_context'] ) ) : ''; 179 $review = isset( $_POST['review_status'] ) ? sanitize_key( wp_unslash( $_POST['review_status'] ) ) : AiSystem::REVIEW_PENDING; 180 $required = isset( $_POST['interaction_disclosure_required'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['interaction_disclosure_required'] ) ); 174 $registry = $this->repository->load(); 175 $id = isset( $_POST['system_id'] ) ? sanitize_text_field( wp_unslash( $_POST['system_id'] ) ) : ''; 176 $name = isset( $_POST['system_name'] ) ? sanitize_text_field( wp_unslash( $_POST['system_name'] ) ) : ''; 177 $type = isset( $_POST['system_type'] ) ? sanitize_key( wp_unslash( $_POST['system_type'] ) ) : ''; 178 $context = isset( $_POST['interaction_context'] ) ? sanitize_textarea_field( wp_unslash( $_POST['interaction_context'] ) ) : ''; 179 $purpose = isset( $_POST['purpose'] ) ? sanitize_textarea_field( wp_unslash( $_POST['purpose'] ) ) : ''; 180 $provider = isset( $_POST['provider'] ) ? sanitize_text_field( wp_unslash( $_POST['provider'] ) ) : ''; 181 $model = isset( $_POST['model'] ) ? sanitize_text_field( wp_unslash( $_POST['model'] ) ) : ''; 182 $audience = isset( $_POST['audience'] ) ? sanitize_textarea_field( wp_unslash( $_POST['audience'] ) ) : ''; 183 $oversight = isset( $_POST['human_oversight'] ) ? sanitize_textarea_field( wp_unslash( $_POST['human_oversight'] ) ) : ''; 184 $data_use = isset( $_POST['data_use_context'] ) ? sanitize_textarea_field( wp_unslash( $_POST['data_use_context'] ) ) : ''; 185 $review = isset( $_POST['review_status'] ) ? sanitize_key( wp_unslash( $_POST['review_status'] ) ) : AiSystem::REVIEW_PENDING; 186 $required = isset( $_POST['interaction_disclosure_required'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['interaction_disclosure_required'] ) ); 181 187 182 188 if ( '' === $name || ! in_array( $type, AiSystem::supported_types(), true ) || ! in_array( $review, AiSystem::supported_review_statuses(), true ) ) { … … 191 197 $now = gmdate( 'c' ); 192 198 $reviewed_at = AiSystem::REVIEW_REVIEWED === $review ? $now : ''; 199 $changes = array( 200 'name' => $name, 201 'type' => $type, 202 'review_status' => $review, 203 'interaction_context' => $context, 204 'purpose' => $purpose, 205 'provider' => $provider, 206 'model' => $model, 207 'audience' => $audience, 208 'human_oversight' => $oversight, 209 'data_use_context' => $data_use, 210 'interaction_disclosure_required' => $required, 211 'updated_at' => $now, 212 'reviewed_at' => $reviewed_at, 213 ); 193 214 194 215 if ( $existing ) { 195 $system = $existing->with_updates( 196 array( 197 'name' => $name, 198 'type' => $type, 199 'review_status' => $review, 200 'interaction_context' => $context, 201 'interaction_disclosure_required' => $required, 202 'updated_at' => $now, 203 'reviewed_at' => $reviewed_at, 204 ) 205 ); 216 $system = $existing->with_updates( $changes ); 206 217 } else { 207 218 $system = new AiSystem( … … 211 222 'manual', 212 223 $required, 213 array( 214 'source_origin' => AiSystem::SOURCE_MANUAL, 215 'status' => AiSystem::STATUS_ACTIVE, 216 'review_status' => $review, 217 'interaction_context' => $context, 218 'created_at' => $now, 219 'updated_at' => $now, 220 'reviewed_at' => $reviewed_at, 224 array_merge( 225 $changes, 226 array( 227 'source_origin' => AiSystem::SOURCE_MANUAL, 228 'status' => AiSystem::STATUS_ACTIVE, 229 'created_at' => $now, 230 ) 221 231 ) 222 232 ); … … 272 282 */ 273 283 private function render_form( $system ): void { 274 $name = $system ? $system->name() : ''; 275 $type = $system ? $system->type() : AiSystem::TYPE_CHATBOT; 276 $context = $system ? $system->interaction_context() : ''; 277 $review = $system ? $system->review_status() : AiSystem::REVIEW_PENDING; 278 $required = $system ? $system->interaction_disclosure_required() : false; 284 $name = $system ? $system->name() : ''; 285 $type = $system ? $system->type() : AiSystem::TYPE_CHATBOT; 286 $context = $system ? $system->interaction_context() : ''; 287 $purpose = $system ? $system->purpose() : ''; 288 $provider = $system ? $system->provider() : ''; 289 $model = $system ? $system->model() : ''; 290 $audience = $system ? $system->audience() : ''; 291 $oversight = $system ? $system->human_oversight() : ''; 292 $data_use = $system ? $system->data_use_context() : ''; 293 $review = $system ? $system->review_status() : AiSystem::REVIEW_PENDING; 294 $required = $system ? $system->interaction_disclosure_required() : false; 279 295 ?> 280 296 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> … … 296 312 <?php endforeach; ?> 297 313 </select> 314 </td> 315 </tr> 316 <tr class="kat-form-section"> 317 <td colspan="2"> 318 <h3><?php echo esc_html__( 'Administrator context', 'kairoseth-ai-transparency' ); ?></h3> 319 <p class="description"><?php echo esc_html__( 'These fields are administrator declarations. Discovery does not infer them from plugin identity or version.', 'kairoseth-ai-transparency' ); ?></p> 320 </td> 321 </tr> 322 <tr> 323 <th scope="row"><label for="kat-purpose"><?php echo esc_html__( 'Purpose', 'kairoseth-ai-transparency' ); ?></label></th> 324 <td> 325 <textarea name="purpose" id="kat-purpose" class="large-text" rows="2" aria-describedby="kat-purpose-description"><?php echo esc_textarea( $purpose ); ?></textarea> 326 <p class="description" id="kat-purpose-description"><?php echo esc_html__( 'Describe what this AI system is used for in this site or workflow.', 'kairoseth-ai-transparency' ); ?></p> 327 </td> 328 </tr> 329 <tr> 330 <th scope="row"><label for="kat-provider"><?php echo esc_html__( 'Provider', 'kairoseth-ai-transparency' ); ?></label></th> 331 <td><input name="provider" id="kat-provider" type="text" class="regular-text" value="<?php echo esc_attr( $provider ); ?>"></td> 332 </tr> 333 <tr> 334 <th scope="row"><label for="kat-model"><?php echo esc_html__( 'Model or model family', 'kairoseth-ai-transparency' ); ?></label></th> 335 <td><input name="model" id="kat-model" type="text" class="regular-text" value="<?php echo esc_attr( $model ); ?>"></td> 336 </tr> 337 <tr> 338 <th scope="row"><label for="kat-audience"><?php echo esc_html__( 'Audience', 'kairoseth-ai-transparency' ); ?></label></th> 339 <td> 340 <textarea name="audience" id="kat-audience" class="large-text" rows="2" aria-describedby="kat-audience-description"><?php echo esc_textarea( $audience ); ?></textarea> 341 <p class="description" id="kat-audience-description"><?php echo esc_html__( 'Describe the people who use, receive, or are affected by this system.', 'kairoseth-ai-transparency' ); ?></p> 342 </td> 343 </tr> 344 <tr> 345 <th scope="row"><label for="kat-human-oversight"><?php echo esc_html__( 'Human oversight', 'kairoseth-ai-transparency' ); ?></label></th> 346 <td> 347 <textarea name="human_oversight" id="kat-human-oversight" class="large-text" rows="3" aria-describedby="kat-human-oversight-description"><?php echo esc_textarea( $oversight ); ?></textarea> 348 <p class="description" id="kat-human-oversight-description"><?php echo esc_html__( 'Describe who reviews, approves, or can intervene in the system outputs or decisions.', 'kairoseth-ai-transparency' ); ?></p> 349 </td> 350 </tr> 351 <tr> 352 <th scope="row"><label for="kat-data-use-context"><?php echo esc_html__( 'Data use context', 'kairoseth-ai-transparency' ); ?></label></th> 353 <td> 354 <textarea name="data_use_context" id="kat-data-use-context" class="large-text" rows="3" aria-describedby="kat-data-use-context-description"><?php echo esc_textarea( $data_use ); ?></textarea> 355 <p class="description" id="kat-data-use-context-description"><?php echo esc_html__( 'Describe the categories of data the workflow uses or receives. Do not enter secrets or credentials.', 'kairoseth-ai-transparency' ); ?></p> 298 356 </td> 299 357 </tr> … … 380 438 'kat_notice' => $notice, 381 439 ), 382 admin_url( ' tools.php' )440 admin_url( 'admin.php' ) 383 441 ) 384 442 ); -
kairoseth-ai-transparency/trunk/src/Admin/class-disclosurepage.php
r3735383 r3738042 119 119 'system' => $system->id(), 120 120 ), 121 admin_url( ' tools.php' )121 admin_url( 'admin.php' ) 122 122 ); 123 123 $shortcode = sprintf( '[kairoseth_ai_disclosure system="%s"]', $system->id() ); -
kairoseth-ai-transparency/trunk/src/Admin/class-discoverypage.php
r3735383 r3738042 9 9 10 10 use Kairoseth\AITransparency\Discovery\AiEngineDetector; 11 use Kairoseth\AITransparency\Discovery\AiPufferDetector; 12 use Kairoseth\AITransparency\Discovery\DiscoveryEngine; 11 13 use Kairoseth\AITransparency\Discovery\DiscoveryResult; 12 14 use Kairoseth\AITransparency\Discovery\WordPressPluginInventory; … … 32 34 33 35 /** 34 * First validated deterministic detector.35 * 36 * @var AiEngineDetector37 */ 38 private $ ai_engine_detector;36 * Deterministic detector orchestration engine. 37 * 38 * @var DiscoveryEngine 39 */ 40 private $discovery_engine; 39 41 40 42 /** … … 42 44 */ 43 45 public function __construct() { 44 $this->repository = new WordPressOptionsRegistryRepository(); 45 $this->inventory = new WordPressPluginInventory(); 46 $this->ai_engine_detector = new AiEngineDetector(); 46 $this->repository = new WordPressOptionsRegistryRepository(); 47 $this->inventory = new WordPressPluginInventory(); 48 $this->discovery_engine = new DiscoveryEngine( 49 array( 50 new AiEngineDetector(), 51 new AiPufferDetector(), 52 ) 53 ); 47 54 } 48 55 … … 100 107 $this->require_permission(); 101 108 102 $result = $this->detect_ai_engine(); 103 $registry = $this->repository->load(); 104 $candidate = $result && $result->supported() ? $result->candidate( gmdate( 'c' ) ) : null; 105 $existing = $candidate ? $registry->find( $candidate->id() ) : null; 109 $results = $this->discover_results(); 110 $registry = $this->repository->load(); 106 111 ?> 107 112 <div class="wrap ai-transparency-admin"> … … 114 119 <h2><?php echo esc_html__( 'Deterministic discovery', 'kairoseth-ai-transparency' ); ?></h2> 115 120 116 <?php if ( null === $result) : ?>117 <p><?php echo esc_html__( ' AI Engine was not detected as active in theWordPress plugin inventory.', 'kairoseth-ai-transparency' ); ?></p>121 <?php if ( empty( $results ) ) : ?> 122 <p><?php echo esc_html__( 'No recognized AI integrations were detected as active in the current WordPress plugin inventory.', 'kairoseth-ai-transparency' ); ?></p> 118 123 <?php else : ?> 119 <?php $this->render_result( $result, null !== $existing ); ?> 124 <div class="ai-transparency-discovery-results"> 125 <?php foreach ( $results as $result ) : ?> 126 <?php 127 $candidate = $result->supported() ? $result->candidate( gmdate( 'c' ) ) : null; 128 $existing = $candidate ? $registry->find( $candidate->id() ) : null; 129 $this->render_result( $result, null !== $existing ); 130 ?> 131 <?php endforeach; ?> 132 </div> 120 133 <?php endif; ?> 121 134 122 135 <p> 123 <a href="<?php echo esc_url( admin_url( ' tools.php?page=ai-transparency' ) ); ?>">136 <a href="<?php echo esc_url( admin_url( 'admin.php?page=ai-transparency' ) ); ?>"> 124 137 <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?> 125 138 </a> … … 136 149 public function handle_accept(): void { 137 150 $this->require_permission(); 138 check_admin_referer( 'ai_transparency_accept_ai_engine', '_kat_discovery_nonce' ); 139 140 $result = $this->detect_ai_engine(); 151 check_admin_referer( 'ai_transparency_accept_discovery', '_kat_discovery_nonce' ); 152 153 $detector_id = isset( $_POST['kat_detector_id'] ) ? sanitize_key( wp_unslash( $_POST['kat_detector_id'] ) ) : ''; 154 $signature = isset( $_POST['kat_source_signature'] ) ? sanitize_key( wp_unslash( $_POST['kat_source_signature'] ) ) : ''; 155 $result = $this->discovery_engine->find( 156 $this->inventory->observations(), 157 $detector_id, 158 $signature 159 ); 160 141 161 if ( null === $result ) { 142 162 $this->redirect_with_notice( 'not_found' ); … … 173 193 */ 174 194 private function render_result( DiscoveryResult $result, bool $already_registered ): void { 175 $evidence = $result->evidence(); 176 $plugin = isset( $evidence['plugin'] ) && is_array( $evidence['plugin'] ) ? $evidence['plugin'] : array(); 177 $status = $result->supported() ? __( 'Supported', 'kairoseth-ai-transparency' ) : __( 'Outside validated boundary', 'kairoseth-ai-transparency' ); 195 $evidence = $result->evidence(); 196 $plugin = isset( $evidence['plugin'] ) && is_array( $evidence['plugin'] ) ? $evidence['plugin'] : array(); 197 $supported_boundary = isset( $evidence['supported_version'] ) ? (string) $evidence['supported_version'] : '—'; 198 $status = $result->supported() ? __( 'Supported', 'kairoseth-ai-transparency' ) : __( 'Outside validated boundary', 'kairoseth-ai-transparency' ); 199 $section_id = sanitize_html_class( 'kat-discovery-' . $result->detector_id() . '-' . substr( $result->source_signature(), 0, 8 ) ); 178 200 ?> 179 <table class="widefat striped ai-transparency-discovery-evidence"> 180 <caption class="screen-reader-text"><?php echo esc_html__( 'Detected integration evidence', 'kairoseth-ai-transparency' ); ?></caption> 181 <tbody> 182 <tr> 183 <th scope="row"><?php echo esc_html__( 'Detected integration', 'kairoseth-ai-transparency' ); ?></th> 184 <td><?php echo esc_html( $result->integration_name() ); ?></td> 185 </tr> 186 <tr> 187 <th scope="row"><?php echo esc_html__( 'Observed version', 'kairoseth-ai-transparency' ); ?></th> 188 <td><?php echo esc_html( $result->observed_version() ); ?></td> 189 </tr> 190 <tr> 191 <th scope="row"><?php echo esc_html__( 'Supported detector boundary', 'kairoseth-ai-transparency' ); ?></th> 192 <td><?php echo esc_html( AiEngineDetector::SUPPORTED_VERSION ); ?></td> 193 </tr> 194 <tr> 195 <th scope="row"><?php echo esc_html__( 'Discovery status', 'kairoseth-ai-transparency' ); ?></th> 196 <td><?php echo esc_html( $status ); ?></td> 197 </tr> 198 <tr> 199 <th scope="row"><?php echo esc_html__( 'Plugin file', 'kairoseth-ai-transparency' ); ?></th> 200 <td><code><?php echo esc_html( isset( $plugin['plugin_file'] ) ? (string) $plugin['plugin_file'] : '' ); ?></code></td> 201 </tr> 202 <tr> 203 <th scope="row"><?php echo esc_html__( 'Text domain', 'kairoseth-ai-transparency' ); ?></th> 204 <td><code><?php echo esc_html( isset( $plugin['text_domain'] ) ? (string) $plugin['text_domain'] : '' ); ?></code></td> 205 </tr> 206 <tr> 207 <th scope="row"><?php echo esc_html__( 'Evidence signature', 'kairoseth-ai-transparency' ); ?></th> 208 <td><code><?php echo esc_html( $result->source_signature() ); ?></code></td> 209 </tr> 210 </tbody> 211 </table> 212 213 <?php if ( ! $result->supported() ) : ?> 214 <p><?php echo esc_html__( 'This AI Engine version is detected but is outside the currently validated detector boundary.', 'kairoseth-ai-transparency' ); ?></p> 215 <p><?php echo esc_html__( 'Review the installed version manually or wait for a validated detector update before adding it through discovery.', 'kairoseth-ai-transparency' ); ?></p> 216 <?php elseif ( $already_registered ) : ?> 217 <p><strong><?php echo esc_html__( 'Already in registry', 'kairoseth-ai-transparency' ); ?></strong></p> 218 <p><?php echo esc_html__( 'This detected integration has already been added to the local registry.', 'kairoseth-ai-transparency' ); ?></p> 219 <?php else : ?> 220 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 221 <input type="hidden" name="action" value="ai_transparency_accept_discovery"> 222 <?php wp_nonce_field( 'ai_transparency_accept_ai_engine', '_kat_discovery_nonce' ); ?> 223 <?php submit_button( __( 'Add to registry', 'kairoseth-ai-transparency' ), 'primary', 'submit', false ); ?> 224 </form> 225 <p><?php echo esc_html__( 'Adding the detected integration creates a local record with pending review status. You must still review its actual use and interaction context.', 'kairoseth-ai-transparency' ); ?></p> 226 <?php endif; ?> 201 <section class="ai-transparency-discovery-result" aria-labelledby="<?php echo esc_attr( $section_id ); ?>"> 202 <h3 id="<?php echo esc_attr( $section_id ); ?>"><?php echo esc_html( $result->integration_name() ); ?></h3> 203 <table class="widefat striped ai-transparency-discovery-evidence"> 204 <caption class="screen-reader-text"><?php echo esc_html__( 'Detected integration evidence', 'kairoseth-ai-transparency' ); ?></caption> 205 <tbody> 206 <tr> 207 <th scope="row"><?php echo esc_html__( 'Detected integration', 'kairoseth-ai-transparency' ); ?></th> 208 <td><?php echo esc_html( $result->integration_name() ); ?></td> 209 </tr> 210 <tr> 211 <th scope="row"><?php echo esc_html__( 'Observed version', 'kairoseth-ai-transparency' ); ?></th> 212 <td><?php echo esc_html( $result->observed_version() ); ?></td> 213 </tr> 214 <tr> 215 <th scope="row"><?php echo esc_html__( 'Supported detector boundary', 'kairoseth-ai-transparency' ); ?></th> 216 <td><?php echo esc_html( $supported_boundary ); ?></td> 217 </tr> 218 <tr> 219 <th scope="row"><?php echo esc_html__( 'Discovery status', 'kairoseth-ai-transparency' ); ?></th> 220 <td><?php echo esc_html( $status ); ?></td> 221 </tr> 222 <tr> 223 <th scope="row"><?php echo esc_html__( 'Plugin file', 'kairoseth-ai-transparency' ); ?></th> 224 <td><code><?php echo esc_html( isset( $plugin['plugin_file'] ) ? (string) $plugin['plugin_file'] : '' ); ?></code></td> 225 </tr> 226 <tr> 227 <th scope="row"><?php echo esc_html__( 'Text domain', 'kairoseth-ai-transparency' ); ?></th> 228 <td><code><?php echo esc_html( isset( $plugin['text_domain'] ) ? (string) $plugin['text_domain'] : '' ); ?></code></td> 229 </tr> 230 <tr> 231 <th scope="row"><?php echo esc_html__( 'Evidence signature', 'kairoseth-ai-transparency' ); ?></th> 232 <td><code><?php echo esc_html( $result->source_signature() ); ?></code></td> 233 </tr> 234 </tbody> 235 </table> 236 237 <?php if ( ! $result->supported() ) : ?> 238 <p><?php echo esc_html__( 'This integration version is detected but is outside its currently validated detector boundary.', 'kairoseth-ai-transparency' ); ?></p> 239 <p><?php echo esc_html__( 'Review the installed version manually or wait for a validated detector update before adding it through discovery.', 'kairoseth-ai-transparency' ); ?></p> 240 <?php elseif ( $already_registered ) : ?> 241 <p><strong><?php echo esc_html__( 'Already in registry', 'kairoseth-ai-transparency' ); ?></strong></p> 242 <p><?php echo esc_html__( 'This detected integration has already been added to the local registry.', 'kairoseth-ai-transparency' ); ?></p> 243 <?php else : ?> 244 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 245 <input type="hidden" name="action" value="ai_transparency_accept_discovery"> 246 <input type="hidden" name="kat_detector_id" value="<?php echo esc_attr( $result->detector_id() ); ?>"> 247 <input type="hidden" name="kat_source_signature" value="<?php echo esc_attr( $result->source_signature() ); ?>"> 248 <?php wp_nonce_field( 'ai_transparency_accept_discovery', '_kat_discovery_nonce' ); ?> 249 <?php submit_button( __( 'Add to registry', 'kairoseth-ai-transparency' ), 'primary', 'submit', false ); ?> 250 </form> 251 <p><?php echo esc_html__( 'Adding the detected integration creates a local record with pending review status. You must still review its actual use and interaction context.', 'kairoseth-ai-transparency' ); ?></p> 252 <?php endif; ?> 253 </section> 227 254 <?php 228 255 } 229 256 230 257 /** 231 * Re-run the AI Engine detector from the current WordPress inventory. 232 * 233 * @return DiscoveryResult|null 234 */ 235 private function detect_ai_engine() { 236 foreach ( $this->inventory->observations() as $observation ) { 237 $result = $this->ai_engine_detector->detect( $observation ); 238 if ( null !== $result ) { 239 return $result; 240 } 241 } 242 243 return null; 258 * Run all registered deterministic detectors against the current inventory. 259 * 260 * @return DiscoveryResult[] 261 */ 262 private function discover_results(): array { 263 return $this->discovery_engine->discover( $this->inventory->observations() ); 244 264 } 245 265 … … 255 275 'added' => __( 'Detected AI integration added to the registry for administrator review.', 'kairoseth-ai-transparency' ), 256 276 'exists' => __( 'The detected integration is already present in the registry.', 'kairoseth-ai-transparency' ), 257 'unsupported' => __( 'The AI Engine observation is outside thecurrently validated detector boundary.', 'kairoseth-ai-transparency' ),258 'not_found' => __( ' AI Engine could not be detected as an active plugin.', 'kairoseth-ai-transparency' ),277 'unsupported' => __( 'The selected integration observation is outside its currently validated detector boundary.', 'kairoseth-ai-transparency' ), 278 'not_found' => __( 'The selected AI integration could not be matched to the current active plugin inventory.', 'kairoseth-ai-transparency' ), 259 279 'save_failed' => __( 'The AI systems registry could not be saved.', 'kairoseth-ai-transparency' ), 260 280 ); … … 264 284 } 265 285 266 $class = 'save_failed' === $notice? 'notice notice-error' : 'notice notice-success';286 $class = in_array( $notice, array( 'save_failed', 'unsupported', 'not_found' ), true ) ? 'notice notice-error' : 'notice notice-success'; 267 287 ?> 268 <div class="<?php echo esc_attr( $class ); ?>" role="<?php echo esc_attr( ' save_failed' === $notice? 'alert' : 'status' ); ?>">288 <div class="<?php echo esc_attr( $class ); ?>" role="<?php echo esc_attr( 'notice notice-error' === $class ? 'alert' : 'status' ); ?>"> 269 289 <p><?php echo esc_html( $messages[ $notice ] ); ?></p> 270 290 </div> … … 295 315 'kat_discovery_notice' => sanitize_key( $notice ), 296 316 ), 297 admin_url( ' tools.php' )317 admin_url( 'admin.php' ) 298 318 ); 299 319 -
kairoseth-ai-transparency/trunk/src/Admin/class-evidenceexportpage.php
r3735383 r3738042 124 124 125 125 <p> 126 <a href="<?php echo esc_url( admin_url( ' tools.php?page=ai-transparency' ) ); ?>">126 <a href="<?php echo esc_url( admin_url( 'admin.php?page=ai-transparency' ) ); ?>"> 127 127 <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?> 128 128 </a> -
kairoseth-ai-transparency/trunk/src/Admin/class-readinesspage.php
r3735383 r3738042 109 109 110 110 <p> 111 <a href="<?php echo esc_url( admin_url( ' tools.php?page=ai-transparency' ) ); ?>">111 <a href="<?php echo esc_url( admin_url( 'admin.php?page=ai-transparency' ) ); ?>"> 112 112 <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?> 113 113 </a> … … 172 172 case Finding::FACT_CONTEXT_MISSING: 173 173 return __( 'No interaction context is recorded for this active AI system.', 'kairoseth-ai-transparency' ); 174 case Finding::FACT_ADMIN_CONTEXT_INCOMPLETE: 175 return __( 'One or more administrator context fields are not documented for this active AI system.', 'kairoseth-ai-transparency' ); 174 176 case Finding::FACT_SYSTEM_ACTIVE: 175 177 return __( 'This AI system is active in the local AI Systems Registry.', 'kairoseth-ai-transparency' ); … … 205 207 case Finding::GUIDANCE_DOCUMENT_CONTEXT: 206 208 return __( 'Document where or how visitors, staff or customers interact with this AI system.', 'kairoseth-ai-transparency' ); 209 case Finding::GUIDANCE_DOCUMENT_ADMIN_CONTEXT: 210 return __( 'Review the missing administrator context fields and document those that are applicable to this system.', 'kairoseth-ai-transparency' ); 207 211 case Finding::GUIDANCE_VERIFY_DISCLOSURE: 208 212 return __( 'Verify that the configured disclosure has an appropriate implementation and placement for the actual workflow.', 'kairoseth-ai-transparency' ); -
kairoseth-ai-transparency/trunk/src/Discovery/class-aienginedetector.php
r3735383 r3738042 11 11 * Detects a validated AI Engine installation from WordPress plugin inventory evidence. 12 12 */ 13 final class AiEngineDetector {13 final class AiEngineDetector implements DetectorInterface { 14 14 public const DETECTOR_ID = 'ai-engine-plugin'; 15 15 public const INTEGRATION_SLUG = 'ai-engine'; -
kairoseth-ai-transparency/trunk/src/Domain/class-aisystem.php
r3735383 r3738042 95 95 96 96 /** 97 * Administrator-declared business or operational purpose. 98 * 99 * @var string 100 */ 101 private $purpose; 102 103 /** 104 * Administrator-declared provider or vendor. 105 * 106 * @var string 107 */ 108 private $provider; 109 110 /** 111 * Administrator-declared model or model family. 112 * 113 * @var string 114 */ 115 private $model; 116 117 /** 118 * Administrator-declared audience or affected user group. 119 * 120 * @var string 121 */ 122 private $audience; 123 124 /** 125 * Administrator-declared human oversight process. 126 * 127 * @var string 128 */ 129 private $human_oversight; 130 131 /** 132 * Administrator-declared data-use context. 133 * 134 * @var string 135 */ 136 private $data_use_context; 137 138 /** 97 139 * Creation timestamp. 98 140 * … … 123 165 * @param string $source Evidence/source identifier. 124 166 * @param bool $interaction_disclosure_required Whether interaction disclosure is required by the configured workflow. 125 * @param array<string, mixed> $metadata Optional lifecycle and originmetadata.167 * @param array<string, mixed> $metadata Optional lifecycle, origin and administrator-authored context metadata. 126 168 * @throws InvalidArgumentException When the record is invalid. 127 169 */ … … 172 214 $this->review_status = $review_status; 173 215 $this->interaction_context = isset( $metadata['interaction_context'] ) ? trim( (string) $metadata['interaction_context'] ) : ''; 216 $this->purpose = isset( $metadata['purpose'] ) ? trim( (string) $metadata['purpose'] ) : ''; 217 $this->provider = isset( $metadata['provider'] ) ? trim( (string) $metadata['provider'] ) : ''; 218 $this->model = isset( $metadata['model'] ) ? trim( (string) $metadata['model'] ) : ''; 219 $this->audience = isset( $metadata['audience'] ) ? trim( (string) $metadata['audience'] ) : ''; 220 $this->human_oversight = isset( $metadata['human_oversight'] ) ? trim( (string) $metadata['human_oversight'] ) : ''; 221 $this->data_use_context = isset( $metadata['data_use_context'] ) ? trim( (string) $metadata['data_use_context'] ) : ''; 174 222 $this->created_at = isset( $metadata['created_at'] ) ? trim( (string) $metadata['created_at'] ) : ''; 175 223 $this->updated_at = isset( $metadata['updated_at'] ) ? trim( (string) $metadata['updated_at'] ) : ''; … … 195 243 'review_status' => $data['review_status'] ?? self::REVIEW_PENDING, 196 244 'interaction_context' => $data['interaction_context'] ?? '', 245 'purpose' => $data['purpose'] ?? '', 246 'provider' => $data['provider'] ?? '', 247 'model' => $data['model'] ?? '', 248 'audience' => $data['audience'] ?? '', 249 'human_oversight' => $data['human_oversight'] ?? '', 250 'data_use_context' => $data['data_use_context'] ?? '', 197 251 'created_at' => $data['created_at'] ?? '', 198 252 'updated_at' => $data['updated_at'] ?? '', … … 291 345 public function interaction_context(): string { 292 346 return $this->interaction_context; 347 } 348 349 /** 350 * Get the administrator-declared purpose. 351 * 352 * @return string 353 */ 354 public function purpose(): string { 355 return $this->purpose; 356 } 357 358 /** 359 * Get the administrator-declared provider. 360 * 361 * @return string 362 */ 363 public function provider(): string { 364 return $this->provider; 365 } 366 367 /** 368 * Get the administrator-declared model or model family. 369 * 370 * @return string 371 */ 372 public function model(): string { 373 return $this->model; 374 } 375 376 /** 377 * Get the administrator-declared audience. 378 * 379 * @return string 380 */ 381 public function audience(): string { 382 return $this->audience; 383 } 384 385 /** 386 * Get the administrator-declared human oversight process. 387 * 388 * @return string 389 */ 390 public function human_oversight(): string { 391 return $this->human_oversight; 392 } 393 394 /** 395 * Get the administrator-declared data-use context. 396 * 397 * @return string 398 */ 399 public function data_use_context(): string { 400 return $this->data_use_context; 293 401 } 294 402 … … 335 443 'review_status' => $this->review_status, 336 444 'interaction_context' => $this->interaction_context, 445 'purpose' => $this->purpose, 446 'provider' => $this->provider, 447 'model' => $this->model, 448 'audience' => $this->audience, 449 'human_oversight' => $this->human_oversight, 450 'data_use_context' => $this->data_use_context, 337 451 'interaction_disclosure_required' => $this->interaction_disclosure_required, 338 452 'created_at' => $this->created_at, -
kairoseth-ai-transparency/trunk/src/Evidence/class-finding.php
r3735383 r3738042 17 17 public const PRIORITY_INFO = 'info'; 18 18 19 public const FACT_REVIEW_PENDING = 'review_pending'; 20 public const FACT_CONTEXT_MISSING = 'interaction_context_missing'; 21 public const FACT_SYSTEM_ACTIVE = 'system_active'; 19 public const FACT_REVIEW_PENDING = 'review_pending'; 20 public const FACT_CONTEXT_MISSING = 'interaction_context_missing'; 21 public const FACT_ADMIN_CONTEXT_INCOMPLETE = 'administrator_context_incomplete'; 22 public const FACT_SYSTEM_ACTIVE = 'system_active'; 22 23 23 24 public const DECLARATION_NONE = ''; 24 25 public const DECLARATION_DISCLOSURE_REQUIRED = 'interaction_disclosure_required'; 25 26 26 public const GUIDANCE_COMPLETE_REVIEW = 'complete_system_review'; 27 public const GUIDANCE_DOCUMENT_CONTEXT = 'document_interaction_context'; 28 public const GUIDANCE_VERIFY_DISCLOSURE = 'verify_disclosure_implementation'; 27 public const GUIDANCE_COMPLETE_REVIEW = 'complete_system_review'; 28 public const GUIDANCE_DOCUMENT_CONTEXT = 'document_interaction_context'; 29 public const GUIDANCE_DOCUMENT_ADMIN_CONTEXT = 'document_administrator_context'; 30 public const GUIDANCE_VERIFY_DISCLOSURE = 'verify_disclosure_implementation'; 29 31 30 32 /** -
kairoseth-ai-transparency/trunk/src/Evidence/class-findingengine.php
r3735383 r3738042 15 15 */ 16 16 final class FindingEngine { 17 public const RULE_REVIEW_PENDING = 'registry_review_pending_v1'; 18 public const RULE_CONTEXT_MISSING = 'interaction_context_missing_v1'; 19 public const RULE_DISCLOSURE_REVIEW = 'configured_disclosure_review_v1'; 17 public const RULE_REVIEW_PENDING = 'registry_review_pending_v1'; 18 public const RULE_CONTEXT_MISSING = 'interaction_context_missing_v1'; 19 public const RULE_ADMIN_CONTEXT_INCOMPLETE = 'administrator_context_incomplete_v1'; 20 public const RULE_DISCLOSURE_REVIEW = 'configured_disclosure_review_v1'; 20 21 21 22 /** … … 68 69 } 69 70 71 $missing_admin_context = $this->missing_admin_context( $system ); 72 if ( AiSystem::REVIEW_REVIEWED === $system->review_status() && ! empty( $missing_admin_context ) ) { 73 $findings[] = $this->build_finding( 74 self::RULE_ADMIN_CONTEXT_INCOMPLETE, 75 'registry_completeness', 76 Finding::PRIORITY_REVIEW, 77 $system, 78 Finding::FACT_ADMIN_CONTEXT_INCOMPLETE, 79 Finding::DECLARATION_NONE, 80 Finding::GUIDANCE_DOCUMENT_ADMIN_CONTEXT, 81 array( 82 'missing_fields' => $missing_admin_context, 83 'status' => $system->status(), 84 ), 85 $generated_at 86 ); 87 } 88 70 89 if ( $system->interaction_disclosure_required() ) { 71 90 $findings[] = $this->build_finding( … … 94 113 95 114 return $findings; 115 } 116 117 /** 118 * Return administrator-authored context fields that remain undocumented. 119 * 120 * @param AiSystem $system AI system record. 121 * @return string[] 122 */ 123 private function missing_admin_context( AiSystem $system ): array { 124 $context = array( 125 'purpose' => $system->purpose(), 126 'provider' => $system->provider(), 127 'model' => $system->model(), 128 'audience' => $system->audience(), 129 'human_oversight' => $system->human_oversight(), 130 'data_use_context' => $system->data_use_context(), 131 ); 132 $missing = array(); 133 134 foreach ( $context as $field => $value ) { 135 if ( '' === $value ) { 136 $missing[] = $field; 137 } 138 } 139 140 return $missing; 96 141 } 97 142 -
kairoseth-ai-transparency/trunk/src/Registry/class-registryschema.php
r3735383 r3738042 15 15 */ 16 16 final class RegistrySchema { 17 public const VERSION = 1;17 public const VERSION = 2; 18 18 19 19 /** … … 67 67 * 68 68 * Version 0 accepted either a raw list of records or an array with a systems key. 69 * Missing Phase 2 metadata is filled by AiSystem defaults during hydration. 69 * Version 1 introduced lifecycle and review metadata. Version 2 adds optional 70 * administrator-authored system context. Missing fields are always normalized 71 * to empty strings rather than inferred from detector evidence. 70 72 * 71 73 * @param mixed $payload Persisted value. … … 105 107 'review_status' => isset( $system_data['review_status'] ) ? (string) $system_data['review_status'] : AiSystem::REVIEW_PENDING, 106 108 'interaction_context' => isset( $system_data['interaction_context'] ) ? (string) $system_data['interaction_context'] : '', 109 'purpose' => isset( $system_data['purpose'] ) ? (string) $system_data['purpose'] : '', 110 'provider' => isset( $system_data['provider'] ) ? (string) $system_data['provider'] : '', 111 'model' => isset( $system_data['model'] ) ? (string) $system_data['model'] : '', 112 'audience' => isset( $system_data['audience'] ) ? (string) $system_data['audience'] : '', 113 'human_oversight' => isset( $system_data['human_oversight'] ) ? (string) $system_data['human_oversight'] : '', 114 'data_use_context' => isset( $system_data['data_use_context'] ) ? (string) $system_data['data_use_context'] : '', 107 115 'interaction_disclosure_required' => ! empty( $system_data['interaction_disclosure_required'] ), 108 116 'created_at' => isset( $system_data['created_at'] ) ? (string) $system_data['created_at'] : '', -
kairoseth-ai-transparency/trunk/src/class-plugin.php
r3735383 r3738042 8 8 namespace Kairoseth\AITransparency; 9 9 10 use Kairoseth\AITransparency\Admin\AdminNavigation; 10 11 use Kairoseth\AITransparency\Admin\AdminPage; 12 use Kairoseth\AITransparency\Admin\DashboardPage; 11 13 use Kairoseth\AITransparency\Admin\DisclosurePage; 12 14 use Kairoseth\AITransparency\Admin\DiscoveryPage; … … 66 68 67 69 if ( is_admin() ) { 68 ( new AdminPage() )->register(); 69 ( new DiscoveryPage() )->register(); 70 ( new ReadinessPage() )->register(); 71 ( new DisclosurePage() )->register(); 72 ( new EvidenceExportPage() )->register(); 73 ( new SupportPage() )->register(); 70 $dashboard_page = new DashboardPage(); 71 $registry_page = new AdminPage(); 72 $discovery_page = new DiscoveryPage(); 73 $readiness_page = new ReadinessPage(); 74 $disclosure_page = new DisclosurePage(); 75 $evidence_export_page = new EvidenceExportPage(); 76 $support_page = new SupportPage(); 77 78 $dashboard_page->register(); 79 $registry_page->register(); 80 $discovery_page->register(); 81 $readiness_page->register(); 82 $disclosure_page->register(); 83 $evidence_export_page->register(); 84 $support_page->register(); 85 86 ( new AdminNavigation( 87 $registry_page, 88 $discovery_page, 89 $readiness_page, 90 $disclosure_page, 91 $evidence_export_page, 92 $support_page 93 ) )->register(); 74 94 } 75 95 }
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)