Plugin Directory

Changeset 3738042


Ignore:
Timestamp:
10/10/2026 12:21:38 PM (10 days ago)
Author:
eduardoyauri
Message:

Release Kairoseth AI Transparency 1.2.0

Location:
kairoseth-ai-transparency
Files:
14 added
28 edited
1 copied

Legend:

Unmodified
Added
Removed
  • kairoseth-ai-transparency/tags/1.2.0/ai-transparency.php

    r3735383 r3738042  
    44 * Plugin URI:        https://kairoseth.com/products/ai-transparency
    55 * Description:       EU AI Act transparency readiness tooling for WordPress, with local AI system inventory, evidence and disclosure workflows.
    6  * Version:           1.0.3
     6 * Version:           1.2.0
    77 * Requires at least: 6.6
    88 * Requires PHP:      7.4
    … …  
    2020}
    2121
    22 define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1.0.3' );
     22define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1.2.0' );
    2323define( 'KAIROSETH_AI_TRANSPARENCY_FILE', __FILE__ );
    2424define( 'KAIROSETH_AI_TRANSPARENCY_PATH', plugin_dir_path( __FILE__ ) );
  • kairoseth-ai-transparency/tags/1.2.0/assets/admin.css

    r3735383 r3738042  
    123123        max-width: 760px;
    124124        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        }
    125344}
    126345
    … …  
    175394                min-height: 44px;
    176395        }
    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  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 1.0.3
     7Stable tag: 1.2.0
    88License: MIT
    99License URI: https://opensource.org/license/mit/
    … …  
    1717The stable release includes:
    1818
    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
     28Legacy 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
     30An 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
     32AI 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
     37Discovery 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.
    2938
    3039AI Readiness generates findings on demand from the current Registry and keeps three concepts separate:
    … …  
    3342* **Administrator declaration** — an explicit administrator state when relevant;
    3443* **Guidance** — a technical review or completion action.
     44
     45Version 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.
    3546
    3647AI Disclosure renders only from server-authoritative reviewed Registry state. The public shortcode is:
    … …  
    4859* a deterministic SHA-256 `snapshot_signature` that excludes generation time;
    4960* 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;
    5162* archived Registry records are retained;
    5263* an empty Registry is valid;
    … …  
    5566* no arbitrary WordPress/plugin option dump or automatic export of credentials, tokens, cookies, nonces, user identities, prompts, conversations, logs or database dumps.
    5667
    57 The privileged export may contain administrator-authored `interaction_context`, so the file may contain confidential operational context and should be handled accordingly.
     68The 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.
    5869
    5970**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.
    … …  
    6576The plugin is local-first. Registry, Discovery, Readiness, Disclosure and Evidence Export do not require Kairoseth or automatically send their state to an external service.
    6677
    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.
    6879
    6980The explicit browser navigation goes to `https://kairoseth.com/custom-requests` and includes only this bounded technical/product context in the URL:
    … …  
    7889* the administrator-selected bounded request type.
    7990
    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.
     91The 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.
    8192
    8293After 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.
    … …  
    901011. Upload the plugin directory to `/wp-content/plugins/` or install the packaged ZIP.
    911022. 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.
     1033. Open **Kairoseth AI Transparency** for the overview dashboard and current technical status.
     1044. Open **Kairoseth AI Transparency > AI Systems Registry** to maintain, search and filter the local Registry.
     1055. Open **Kairoseth AI Transparency > AI Discovery** to review supported deterministic integration evidence.
     1066. Open **Kairoseth AI Transparency > AI Readiness** to review current technical findings.
     1077. Open **Kairoseth AI Transparency > AI Disclosure** to review disclosure readiness and copy a shortcode for an eligible system.
     1088. Open **Kairoseth AI Transparency > AI Evidence Export** to download the current site-local JSON evidence snapshot.
     1099. Optionally open **Kairoseth AI Transparency > AI Transparency Support** for an explicit privacy-bounded handoff to Kairoseth support/custom requests.
    98110
    99111== Frequently Asked Questions ==
    … …  
    109121= What does AI Discovery detect? =
    110122
    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.
     123Version 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
     127In 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.
    112128
    113129= What does AI Readiness report? =
    114130
    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 and does not make legal compliance decisions.
     131The 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.
    116132
    117133= What is the Evidence Export snapshot signature? =
    118134
    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.
     135It 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.
    120136
    121137= Is Evidence Export stored or uploaded? =
    … …  
    123139No. 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.
    124140
     141= What happens to my Registry if I update from an earlier version? =
     142
     143Version 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
    125145= What happens to my Registry if I deactivate or remove the plugin? =
    126146
    … …  
    129149= How is Multisite handled? =
    130150
    131 Registry and Evidence Export follow the authoritative current blog/site context. The first export does not aggregate the full network. A network uninstall removes only the plugin-owned Registry option from each existing site.
     151Registry 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.
    132152
    133153= Does it detect whether any article was written by AI? =
    … …  
    136156
    137157== 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.
    138178
    139179= 1.0.3 =
    … …  
    170210== Upgrade Notice ==
    171211
     212= 1.2.0 =
     213Adds 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 =
     216Adds 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
    172218= 1.0.3 =
    173219Ownership-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  
    125125                                                                                'system' => $system->id(),
    126126                                                                        ),
    127                                                                         admin_url( 'tools.php' )
     127                                                                        admin_url( 'admin.php' )
    128128                                                                );
    129129                                                                ?>
    … …  
    172172                check_admin_referer( 'kairoseth_ai_transparency_save_system', '_kat_nonce' );
    173173
    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'] ) );
    181187
    182188                if ( '' === $name || ! in_array( $type, AiSystem::supported_types(), true ) || ! in_array( $review, AiSystem::supported_review_statuses(), true ) ) {
    … …  
    191197                $now         = gmdate( 'c' );
    192198                $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                );
    193214
    194215                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 );
    206217                } else {
    207218                        $system = new AiSystem(
    … …  
    211222                                'manual',
    212223                                $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                                        )
    221231                                )
    222232                        );
    … …  
    272282         */
    273283        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;
    279295                ?>
    280296                <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
    … …  
    296312                                                        <?php endforeach; ?>
    297313                                                </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>
    298356                                        </td>
    299357                                </tr>
    … …  
    380438                                        'kat_notice' => $notice,
    381439                                ),
    382                                 admin_url( 'tools.php' )
     440                                admin_url( 'admin.php' )
    383441                        )
    384442                );
  • kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-disclosurepage.php

    r3735383 r3738042  
    119119                                                                                'system' => $system->id(),
    120120                                                                        ),
    121                                                                         admin_url( 'tools.php' )
     121                                                                        admin_url( 'admin.php' )
    122122                                                                );
    123123                                                                $shortcode = sprintf( '[kairoseth_ai_disclosure system="%s"]', $system->id() );
  • kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-discoverypage.php

    r3735383 r3738042  
    99
    1010use Kairoseth\AITransparency\Discovery\AiEngineDetector;
     11use Kairoseth\AITransparency\Discovery\AiPufferDetector;
     12use Kairoseth\AITransparency\Discovery\DiscoveryEngine;
    1113use Kairoseth\AITransparency\Discovery\DiscoveryResult;
    1214use Kairoseth\AITransparency\Discovery\WordPressPluginInventory;
    … …  
    3234
    3335        /**
    34          * First validated deterministic detector.
    35          *
    36          * @var AiEngineDetector
    37          */
    38         private $ai_engine_detector;
     36         * Deterministic detector orchestration engine.
     37         *
     38         * @var DiscoveryEngine
     39         */
     40        private $discovery_engine;
    3941
    4042        /**
    … …  
    4244         */
    4345        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                );
    4754        }
    4855
    … …  
    100107                $this->require_permission();
    101108
    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();
    106111                ?>
    107112                <div class="wrap ai-transparency-admin">
    … …  
    114119                        <h2><?php echo esc_html__( 'Deterministic discovery', 'kairoseth-ai-transparency' ); ?></h2>
    115120
    116                         <?php if ( null === $result ) : ?>
    117                                 <p><?php echo esc_html__( 'AI Engine was not detected as active in the WordPress 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>
    118123                        <?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>
    120133                        <?php endif; ?>
    121134
    122135                        <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' ) ); ?>">
    124137                                        <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?>
    125138                                </a>
    … …  
    136149        public function handle_accept(): void {
    137150                $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
    141161                if ( null === $result ) {
    142162                        $this->redirect_with_notice( 'not_found' );
    … …  
    173193         */
    174194        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 ) );
    178200                ?>
    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>
    227254                <?php
    228255        }
    229256
    230257        /**
    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() );
    244264        }
    245265
    … …  
    255275                        'added'       => __( 'Detected AI integration added to the registry for administrator review.', 'kairoseth-ai-transparency' ),
    256276                        'exists'      => __( 'The detected integration is already present in the registry.', 'kairoseth-ai-transparency' ),
    257                         'unsupported' => __( 'The AI Engine observation is outside the currently 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' ),
    259279                        'save_failed' => __( 'The AI systems registry could not be saved.', 'kairoseth-ai-transparency' ),
    260280                );
    … …  
    264284                }
    265285
    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';
    267287                ?>
    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' ); ?>">
    269289                        <p><?php echo esc_html( $messages[ $notice ] ); ?></p>
    270290                </div>
    … …  
    295315                                'kat_discovery_notice' => sanitize_key( $notice ),
    296316                        ),
    297                         admin_url( 'tools.php' )
     317                        admin_url( 'admin.php' )
    298318                );
    299319
  • kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-evidenceexportpage.php

    r3735383 r3738042  
    124124
    125125                        <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' ) ); ?>">
    127127                                        <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?>
    128128                                </a>
  • kairoseth-ai-transparency/tags/1.2.0/src/Admin/class-readinesspage.php

    r3735383 r3738042  
    109109
    110110                        <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' ) ); ?>">
    112112                                        <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?>
    113113                                </a>
    … …  
    172172                        case Finding::FACT_CONTEXT_MISSING:
    173173                                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' );
    174176                        case Finding::FACT_SYSTEM_ACTIVE:
    175177                                return __( 'This AI system is active in the local AI Systems Registry.', 'kairoseth-ai-transparency' );
    … …  
    205207                        case Finding::GUIDANCE_DOCUMENT_CONTEXT:
    206208                                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' );
    207211                        case Finding::GUIDANCE_VERIFY_DISCLOSURE:
    208212                                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  
    1111 * Detects a validated AI Engine installation from WordPress plugin inventory evidence.
    1212 */
    13 final class AiEngineDetector {
     13final class AiEngineDetector implements DetectorInterface {
    1414        public const DETECTOR_ID       = 'ai-engine-plugin';
    1515        public const INTEGRATION_SLUG  = 'ai-engine';
  • kairoseth-ai-transparency/tags/1.2.0/src/Domain/class-aisystem.php

    r3735383 r3738042  
    9595
    9696        /**
     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        /**
    97139         * Creation timestamp.
    98140         *
    … …  
    123165         * @param string               $source Evidence/source identifier.
    124166         * @param bool                 $interaction_disclosure_required Whether interaction disclosure is required by the configured workflow.
    125          * @param array<string, mixed> $metadata Optional lifecycle and origin metadata.
     167         * @param array<string, mixed> $metadata Optional lifecycle, origin and administrator-authored context metadata.
    126168         * @throws InvalidArgumentException When the record is invalid.
    127169         */
    … …  
    172214                $this->review_status                   = $review_status;
    173215                $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'] ) : '';
    174222                $this->created_at                      = isset( $metadata['created_at'] ) ? trim( (string) $metadata['created_at'] ) : '';
    175223                $this->updated_at                      = isset( $metadata['updated_at'] ) ? trim( (string) $metadata['updated_at'] ) : '';
    … …  
    195243                                'review_status'       => $data['review_status'] ?? self::REVIEW_PENDING,
    196244                                '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'] ?? '',
    197251                                'created_at'          => $data['created_at'] ?? '',
    198252                                'updated_at'          => $data['updated_at'] ?? '',
    … …  
    291345        public function interaction_context(): string {
    292346                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;
    293401        }
    294402
    … …  
    335443                        'review_status'                   => $this->review_status,
    336444                        '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,
    337451                        'interaction_disclosure_required' => $this->interaction_disclosure_required,
    338452                        'created_at'                      => $this->created_at,
  • kairoseth-ai-transparency/tags/1.2.0/src/Evidence/class-finding.php

    r3735383 r3738042  
    1717        public const PRIORITY_INFO   = 'info';
    1818
    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';
    2223
    2324        public const DECLARATION_NONE                = '';
    2425        public const DECLARATION_DISCLOSURE_REQUIRED = 'interaction_disclosure_required';
    2526
    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';
    2931
    3032        /**
  • kairoseth-ai-transparency/tags/1.2.0/src/Evidence/class-findingengine.php

    r3735383 r3738042  
    1515 */
    1616final 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';
    2021
    2122        /**
    … …  
    6869                        }
    6970
     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
    7089                        if ( $system->interaction_disclosure_required() ) {
    7190                                $findings[] = $this->build_finding(
    … …  
    94113
    95114                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;
    96141        }
    97142
  • kairoseth-ai-transparency/tags/1.2.0/src/Registry/class-registryschema.php

    r3735383 r3738042  
    1515 */
    1616final class RegistrySchema {
    17         public const VERSION = 1;
     17        public const VERSION = 2;
    1818
    1919        /**
    … …  
    6767         *
    6868         * 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.
    7072         *
    7173         * @param mixed $payload Persisted value.
    … …  
    105107                                'review_status'                   => isset( $system_data['review_status'] ) ? (string) $system_data['review_status'] : AiSystem::REVIEW_PENDING,
    106108                                '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'] : '',
    107115                                'interaction_disclosure_required' => ! empty( $system_data['interaction_disclosure_required'] ),
    108116                                '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  
    88namespace Kairoseth\AITransparency;
    99
     10use Kairoseth\AITransparency\Admin\AdminNavigation;
    1011use Kairoseth\AITransparency\Admin\AdminPage;
     12use Kairoseth\AITransparency\Admin\DashboardPage;
    1113use Kairoseth\AITransparency\Admin\DisclosurePage;
    1214use Kairoseth\AITransparency\Admin\DiscoveryPage;
    … …  
    6668
    6769                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();
    7494                }
    7595        }
  • kairoseth-ai-transparency/trunk/ai-transparency.php

    r3735383 r3738042  
    44 * Plugin URI:        https://kairoseth.com/products/ai-transparency
    55 * Description:       EU AI Act transparency readiness tooling for WordPress, with local AI system inventory, evidence and disclosure workflows.
    6  * Version:           1.0.3
     6 * Version:           1.2.0
    77 * Requires at least: 6.6
    88 * Requires PHP:      7.4
    … …  
    2020}
    2121
    22 define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1.0.3' );
     22define( 'KAIROSETH_AI_TRANSPARENCY_VERSION', '1.2.0' );
    2323define( 'KAIROSETH_AI_TRANSPARENCY_FILE', __FILE__ );
    2424define( 'KAIROSETH_AI_TRANSPARENCY_PATH', plugin_dir_path( __FILE__ ) );
  • kairoseth-ai-transparency/trunk/assets/admin.css

    r3735383 r3738042  
    123123        max-width: 760px;
    124124        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        }
    125344}
    126345
    … …  
    175394                min-height: 44px;
    176395        }
    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  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 1.0.3
     7Stable tag: 1.2.0
    88License: MIT
    99License URI: https://opensource.org/license/mit/
    … …  
    1717The stable release includes:
    1818
    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
     28Legacy 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
     30An 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
     32AI 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
     37Discovery 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.
    2938
    3039AI Readiness generates findings on demand from the current Registry and keeps three concepts separate:
    … …  
    3342* **Administrator declaration** — an explicit administrator state when relevant;
    3443* **Guidance** — a technical review or completion action.
     44
     45Version 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.
    3546
    3647AI Disclosure renders only from server-authoritative reviewed Registry state. The public shortcode is:
    … …  
    4859* a deterministic SHA-256 `snapshot_signature` that excludes generation time;
    4960* 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;
    5162* archived Registry records are retained;
    5263* an empty Registry is valid;
    … …  
    5566* no arbitrary WordPress/plugin option dump or automatic export of credentials, tokens, cookies, nonces, user identities, prompts, conversations, logs or database dumps.
    5667
    57 The privileged export may contain administrator-authored `interaction_context`, so the file may contain confidential operational context and should be handled accordingly.
     68The 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.
    5869
    5970**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.
    … …  
    6576The plugin is local-first. Registry, Discovery, Readiness, Disclosure and Evidence Export do not require Kairoseth or automatically send their state to an external service.
    6677
    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.
    6879
    6980The explicit browser navigation goes to `https://kairoseth.com/custom-requests` and includes only this bounded technical/product context in the URL:
    … …  
    7889* the administrator-selected bounded request type.
    7990
    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.
     91The 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.
    8192
    8293After 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.
    … …  
    901011. Upload the plugin directory to `/wp-content/plugins/` or install the packaged ZIP.
    911022. 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.
     1033. Open **Kairoseth AI Transparency** for the overview dashboard and current technical status.
     1044. Open **Kairoseth AI Transparency > AI Systems Registry** to maintain, search and filter the local Registry.
     1055. Open **Kairoseth AI Transparency > AI Discovery** to review supported deterministic integration evidence.
     1066. Open **Kairoseth AI Transparency > AI Readiness** to review current technical findings.
     1077. Open **Kairoseth AI Transparency > AI Disclosure** to review disclosure readiness and copy a shortcode for an eligible system.
     1088. Open **Kairoseth AI Transparency > AI Evidence Export** to download the current site-local JSON evidence snapshot.
     1099. Optionally open **Kairoseth AI Transparency > AI Transparency Support** for an explicit privacy-bounded handoff to Kairoseth support/custom requests.
    98110
    99111== Frequently Asked Questions ==
    … …  
    109121= What does AI Discovery detect? =
    110122
    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.
     123Version 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
     127In 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.
    112128
    113129= What does AI Readiness report? =
    114130
    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 and does not make legal compliance decisions.
     131The 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.
    116132
    117133= What is the Evidence Export snapshot signature? =
    118134
    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.
     135It 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.
    120136
    121137= Is Evidence Export stored or uploaded? =
    … …  
    123139No. 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.
    124140
     141= What happens to my Registry if I update from an earlier version? =
     142
     143Version 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
    125145= What happens to my Registry if I deactivate or remove the plugin? =
    126146
    … …  
    129149= How is Multisite handled? =
    130150
    131 Registry and Evidence Export follow the authoritative current blog/site context. The first export does not aggregate the full network. A network uninstall removes only the plugin-owned Registry option from each existing site.
     151Registry 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.
    132152
    133153= Does it detect whether any article was written by AI? =
    … …  
    136156
    137157== 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.
    138178
    139179= 1.0.3 =
    … …  
    170210== Upgrade Notice ==
    171211
     212= 1.2.0 =
     213Adds 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 =
     216Adds 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
    172218= 1.0.3 =
    173219Ownership-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  
    125125                                                                                'system' => $system->id(),
    126126                                                                        ),
    127                                                                         admin_url( 'tools.php' )
     127                                                                        admin_url( 'admin.php' )
    128128                                                                );
    129129                                                                ?>
    … …  
    172172                check_admin_referer( 'kairoseth_ai_transparency_save_system', '_kat_nonce' );
    173173
    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'] ) );
    181187
    182188                if ( '' === $name || ! in_array( $type, AiSystem::supported_types(), true ) || ! in_array( $review, AiSystem::supported_review_statuses(), true ) ) {
    … …  
    191197                $now         = gmdate( 'c' );
    192198                $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                );
    193214
    194215                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 );
    206217                } else {
    207218                        $system = new AiSystem(
    … …  
    211222                                'manual',
    212223                                $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                                        )
    221231                                )
    222232                        );
    … …  
    272282         */
    273283        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;
    279295                ?>
    280296                <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
    … …  
    296312                                                        <?php endforeach; ?>
    297313                                                </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>
    298356                                        </td>
    299357                                </tr>
    … …  
    380438                                        'kat_notice' => $notice,
    381439                                ),
    382                                 admin_url( 'tools.php' )
     440                                admin_url( 'admin.php' )
    383441                        )
    384442                );
  • kairoseth-ai-transparency/trunk/src/Admin/class-disclosurepage.php

    r3735383 r3738042  
    119119                                                                                'system' => $system->id(),
    120120                                                                        ),
    121                                                                         admin_url( 'tools.php' )
     121                                                                        admin_url( 'admin.php' )
    122122                                                                );
    123123                                                                $shortcode = sprintf( '[kairoseth_ai_disclosure system="%s"]', $system->id() );
  • kairoseth-ai-transparency/trunk/src/Admin/class-discoverypage.php

    r3735383 r3738042  
    99
    1010use Kairoseth\AITransparency\Discovery\AiEngineDetector;
     11use Kairoseth\AITransparency\Discovery\AiPufferDetector;
     12use Kairoseth\AITransparency\Discovery\DiscoveryEngine;
    1113use Kairoseth\AITransparency\Discovery\DiscoveryResult;
    1214use Kairoseth\AITransparency\Discovery\WordPressPluginInventory;
    … …  
    3234
    3335        /**
    34          * First validated deterministic detector.
    35          *
    36          * @var AiEngineDetector
    37          */
    38         private $ai_engine_detector;
     36         * Deterministic detector orchestration engine.
     37         *
     38         * @var DiscoveryEngine
     39         */
     40        private $discovery_engine;
    3941
    4042        /**
    … …  
    4244         */
    4345        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                );
    4754        }
    4855
    … …  
    100107                $this->require_permission();
    101108
    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();
    106111                ?>
    107112                <div class="wrap ai-transparency-admin">
    … …  
    114119                        <h2><?php echo esc_html__( 'Deterministic discovery', 'kairoseth-ai-transparency' ); ?></h2>
    115120
    116                         <?php if ( null === $result ) : ?>
    117                                 <p><?php echo esc_html__( 'AI Engine was not detected as active in the WordPress 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>
    118123                        <?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>
    120133                        <?php endif; ?>
    121134
    122135                        <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' ) ); ?>">
    124137                                        <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?>
    125138                                </a>
    … …  
    136149        public function handle_accept(): void {
    137150                $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
    141161                if ( null === $result ) {
    142162                        $this->redirect_with_notice( 'not_found' );
    … …  
    173193         */
    174194        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 ) );
    178200                ?>
    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>
    227254                <?php
    228255        }
    229256
    230257        /**
    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() );
    244264        }
    245265
    … …  
    255275                        'added'       => __( 'Detected AI integration added to the registry for administrator review.', 'kairoseth-ai-transparency' ),
    256276                        'exists'      => __( 'The detected integration is already present in the registry.', 'kairoseth-ai-transparency' ),
    257                         'unsupported' => __( 'The AI Engine observation is outside the currently 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' ),
    259279                        'save_failed' => __( 'The AI systems registry could not be saved.', 'kairoseth-ai-transparency' ),
    260280                );
    … …  
    264284                }
    265285
    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';
    267287                ?>
    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' ); ?>">
    269289                        <p><?php echo esc_html( $messages[ $notice ] ); ?></p>
    270290                </div>
    … …  
    295315                                'kat_discovery_notice' => sanitize_key( $notice ),
    296316                        ),
    297                         admin_url( 'tools.php' )
     317                        admin_url( 'admin.php' )
    298318                );
    299319
  • kairoseth-ai-transparency/trunk/src/Admin/class-evidenceexportpage.php

    r3735383 r3738042  
    124124
    125125                        <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' ) ); ?>">
    127127                                        <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?>
    128128                                </a>
  • kairoseth-ai-transparency/trunk/src/Admin/class-readinesspage.php

    r3735383 r3738042  
    109109
    110110                        <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' ) ); ?>">
    112112                                        <?php echo esc_html__( 'Open AI Systems Registry', 'kairoseth-ai-transparency' ); ?>
    113113                                </a>
    … …  
    172172                        case Finding::FACT_CONTEXT_MISSING:
    173173                                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' );
    174176                        case Finding::FACT_SYSTEM_ACTIVE:
    175177                                return __( 'This AI system is active in the local AI Systems Registry.', 'kairoseth-ai-transparency' );
    … …  
    205207                        case Finding::GUIDANCE_DOCUMENT_CONTEXT:
    206208                                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' );
    207211                        case Finding::GUIDANCE_VERIFY_DISCLOSURE:
    208212                                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  
    1111 * Detects a validated AI Engine installation from WordPress plugin inventory evidence.
    1212 */
    13 final class AiEngineDetector {
     13final class AiEngineDetector implements DetectorInterface {
    1414        public const DETECTOR_ID       = 'ai-engine-plugin';
    1515        public const INTEGRATION_SLUG  = 'ai-engine';
  • kairoseth-ai-transparency/trunk/src/Domain/class-aisystem.php

    r3735383 r3738042  
    9595
    9696        /**
     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        /**
    97139         * Creation timestamp.
    98140         *
    … …  
    123165         * @param string               $source Evidence/source identifier.
    124166         * @param bool                 $interaction_disclosure_required Whether interaction disclosure is required by the configured workflow.
    125          * @param array<string, mixed> $metadata Optional lifecycle and origin metadata.
     167         * @param array<string, mixed> $metadata Optional lifecycle, origin and administrator-authored context metadata.
    126168         * @throws InvalidArgumentException When the record is invalid.
    127169         */
    … …  
    172214                $this->review_status                   = $review_status;
    173215                $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'] ) : '';
    174222                $this->created_at                      = isset( $metadata['created_at'] ) ? trim( (string) $metadata['created_at'] ) : '';
    175223                $this->updated_at                      = isset( $metadata['updated_at'] ) ? trim( (string) $metadata['updated_at'] ) : '';
    … …  
    195243                                'review_status'       => $data['review_status'] ?? self::REVIEW_PENDING,
    196244                                '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'] ?? '',
    197251                                'created_at'          => $data['created_at'] ?? '',
    198252                                'updated_at'          => $data['updated_at'] ?? '',
    … …  
    291345        public function interaction_context(): string {
    292346                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;
    293401        }
    294402
    … …  
    335443                        'review_status'                   => $this->review_status,
    336444                        '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,
    337451                        'interaction_disclosure_required' => $this->interaction_disclosure_required,
    338452                        'created_at'                      => $this->created_at,
  • kairoseth-ai-transparency/trunk/src/Evidence/class-finding.php

    r3735383 r3738042  
    1717        public const PRIORITY_INFO   = 'info';
    1818
    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';
    2223
    2324        public const DECLARATION_NONE                = '';
    2425        public const DECLARATION_DISCLOSURE_REQUIRED = 'interaction_disclosure_required';
    2526
    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';
    2931
    3032        /**
  • kairoseth-ai-transparency/trunk/src/Evidence/class-findingengine.php

    r3735383 r3738042  
    1515 */
    1616final 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';
    2021
    2122        /**
    … …  
    6869                        }
    6970
     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
    7089                        if ( $system->interaction_disclosure_required() ) {
    7190                                $findings[] = $this->build_finding(
    … …  
    94113
    95114                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;
    96141        }
    97142
  • kairoseth-ai-transparency/trunk/src/Registry/class-registryschema.php

    r3735383 r3738042  
    1515 */
    1616final class RegistrySchema {
    17         public const VERSION = 1;
     17        public const VERSION = 2;
    1818
    1919        /**
    … …  
    6767         *
    6868         * 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.
    7072         *
    7173         * @param mixed $payload Persisted value.
    … …  
    105107                                'review_status'                   => isset( $system_data['review_status'] ) ? (string) $system_data['review_status'] : AiSystem::REVIEW_PENDING,
    106108                                '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'] : '',
    107115                                'interaction_disclosure_required' => ! empty( $system_data['interaction_disclosure_required'] ),
    108116                                'created_at'                      => isset( $system_data['created_at'] ) ? (string) $system_data['created_at'] : '',
  • kairoseth-ai-transparency/trunk/src/class-plugin.php

    r3735383 r3738042  
    88namespace Kairoseth\AITransparency;
    99
     10use Kairoseth\AITransparency\Admin\AdminNavigation;
    1011use Kairoseth\AITransparency\Admin\AdminPage;
     12use Kairoseth\AITransparency\Admin\DashboardPage;
    1113use Kairoseth\AITransparency\Admin\DisclosurePage;
    1214use Kairoseth\AITransparency\Admin\DiscoveryPage;
    … …  
    6668
    6769                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();
    7494                }
    7595        }
Note: See TracChangeset for help on using the changeset viewer.