{
  "schema_version": "1.0",
  "base_url": "https://outcome-evidence-map.netlify.app",
  "api_path": "/.netlify/functions/evidence-bridge",
  "catalogue_path": "/connected-catalogue.json",
  "client_deployment_path": "/field/",
  "browser_origin_policy": "Same origin; no public cross-origin write endpoint",
  "courses": [
    "MM12",
    "COM11",
    "IBDS"
  ],
  "achievement": [
    "IE",
    "Beginning",
    "Developing",
    "Secure",
    "Extending"
  ],
  "support": [
    "Guided",
    "Supported",
    "Independent",
    "Transfer"
  ],
  "authentication": {
    "teacher_login": {
      "method": "POST",
      "action": "login",
      "body": {
        "access_key": "TEACHER_ENTERS_PRIVATELY"
      },
      "session": "HttpOnly Secure SameSite=Strict cookie; four hours"
    },
    "google_server": "Bearer EVIDENCE_MAP_INGEST_SECRET from existing Apps Script properties; never in browser",
    "reader_server": "Bearer EVIDENCE_MAP_READ_SECRET; cannot create judgments",
    "teacher_server": "Bearer EVIDENCE_MAP_TEACHER_SECRET; never embedded in field app",
    "logout": {
      "method": "POST",
      "action": "logout",
      "body": {}
    }
  },
  "actions": {
    "status": {
      "method": "GET",
      "response_fields": [
        "schema_version",
        "role",
        "plan_version",
        "reporting",
        "expires_at"
      ]
    },
    "snapshot": {
      "method": "GET",
      "query": {
        "course_id": "MM12"
      },
      "response_fields": [
        "schema_version",
        "role",
        "course_id",
        "events",
        "progress",
        "roster",
        "rubrics",
        "plan_version",
        "generated_at"
      ]
    },
    "events": {
      "method": "POST",
      "body": {
        "events": []
      },
      "limit": 100,
      "response": {
        "results": {
          "event_id": {
            "ok": true,
            "record_id": "event_id",
            "revision_id": "content_hash",
            "dedup": false,
            "mapping_status": "mapped"
          }
        }
      }
    },
    "roster": {
      "method": "POST",
      "owner": "Google only",
      "body": {
        "course_id": "MM12",
        "learners": [
          {
            "learner_id": "lrn_fixture_01",
            "display_name": "Fictional learner",
            "active": true,
            "powerschool_id": ""
          }
        ]
      }
    },
    "progress": {
      "method": "GET",
      "query": {
        "course_id": "MM12"
      },
      "purpose": "Read-only mirror to Google; no event echo"
    },
    "review": {
      "method": "POST",
      "owner": "Teacher only",
      "body": {
        "review_id": "uuid-generated-once",
        "course_id": "MM12",
        "learner_id": "lrn_fixture_01",
        "outcome_id": "ns-mm12-2015-1.1",
        "achievement": "Secure",
        "support": "Guided",
        "rationale": "Reason grounded in the selected evidence",
        "comment": "Teacher-written reporting comment",
        "evidence_ids": [
          "fixture-event-01"
        ],
        "parents": []
      }
    },
    "rubric": {
      "method": "POST",
      "owner": "Teacher only",
      "purpose": "Approve an existing catalogue rubric after review",
      "body": {
        "course_id": "MM12",
        "rubric_id": "MM12-TRUCK-AD-R1",
        "confirmed": true
      }
    },
    "field-events": {
      "method": "GET",
      "owner": "Google only",
      "query": {
        "course_id": "MM12",
        "limit": 100,
        "cursor": "optional prior next_cursor"
      },
      "limit_max": 500,
      "response_fields": [
        "course_id",
        "events",
        "has_more",
        "next_cursor",
        "total_field_events",
        "generated_at"
      ],
      "cursor_rule": "Restart from first page on HTTP 409; upsert safely by course_id and event_id."
    },
    "rubrics": {
      "method": "GET",
      "owner": "Teacher or Google",
      "query": {
        "course_id": "MM12"
      },
      "purpose": "Read shared approved immutable rubric versions."
    },
    "rubric-publish": {
      "method": "POST",
      "owner": "Teacher only",
      "required_body_fields": [
        "id",
        "course",
        "project_id",
        "title",
        "criteria",
        "confirmed"
      ],
      "confirmation": "confirmed must be true; every criterion requires an ID, name, mapped outcomes and all four descriptors.",
      "version": "Server content-addressed v1.<hash>; catalogue rubric IDs must be duplicated under a new ID.",
      "response_fields": [
        "ok",
        "rubric",
        "dedup"
      ],
      "drafts": "Drafts are local to the client; no shared draft save API."
    },
    "mirror-status": {
      "methods": [
        "GET",
        "POST"
      ],
      "GET": {
        "owner": "Teacher",
        "query": {
          "course_id": "MM12"
        },
        "response_fields": [
          "course_id",
          "receipts"
        ]
      },
      "POST": {
        "owner": "Google",
        "body": {
          "course_id": "MM12",
          "receipts": [
            {
              "event_id": "fictional-field-event",
              "revision_id": "exact revision written to Sheet"
            }
          ]
        },
        "limit": 500
      },
      "rule": "Only acknowledge after the Sheet write and flush succeeds; the client compares the exact revision."
    }
  },
  "field_event_example": {
    "schema_version": "1.0",
    "event_id": "fixture-event-01",
    "source_revision": 1,
    "stream": "evidence",
    "learner_id": "lrn_fixture_01",
    "course_id": "MM12",
    "project_id": "PS-TRUCK-AD",
    "canonical_id": "TA-OBS-01",
    "step_id": "TA-S03",
    "rubric_id": "MM12-TRUCK-AD-R1",
    "rubric_version": "2026-09-23.1",
    "criterion_id": "TA-MM-C1",
    "outcome_codes_raw": "MM12-1.1;MM12-1.3",
    "tutorial_skill_ids": [
      "PS-LAY-01"
    ],
    "evidence_type_raw": "Observation+Conversation+Product",
    "response_value": "Learner located the mask and explained the reversible edit.",
    "teacher_note": "Visible edge repair and accurate explanation.",
    "level_raw": "3 - Secure",
    "independence_raw": "Guided",
    "artifact_url": "https://example.com/fictional-truck-ad",
    "teacher_verified": false,
    "timestamp": "2026-09-23T12:00:00Z"
  },
  "not_implemented": [
    "file/photo upload API",
    "protected cross-device audio upload, download and retention",
    "shared rubric-draft save/read API",
    "live Gemini assistance (server key and model not configured)",
    "PowerSchool-specific import adapter",
    "multi-teacher accounts and per-class permissions",
    "physical iPad Safari/offline/microphone certification"
  ],
  "conflict_policy": "Append-only revisions. Conflicting event revisions cannot support a judgment. Conflicting judgment heads have no reportable achievement until explicitly reconciled.",
  "curriculum_rule": "A planned sprint is not proof of a completed teaching package. Use catalogue status and actual materials.",
  "field_capture": {
    "supported_methods": [
      "Observation",
      "Conversation",
      "Product"
    ],
    "combined_methods": "Exact names joined with + in evidence_type_raw; every selected method is retained",
    "product_reference": "Optional artifact_url; HTTPS only; uploaded file bytes are not supported",
    "achievement": "Optional suggested level, not a final outcome judgment",
    "approval": "Server-approved exact rubric ID/version and matching criterion/outcome mapping required"
  },
  "google_sheet_connection": {
    "spreadsheet_id": "1hGXzxde_q7UmKUFYKub3N_j_bFTMRrZqIUxTCIpciwc",
    "prepared_tabs": {
      "Field App Setup": 1275286835,
      "Field Evidence": 56321294,
      "Rubric Library": 905800740
    },
    "prepared_schema_reference": "GOOGLE_SHEET_FIELD_SETUP.json",
    "existing": [
      "Google roster and Form evidence sent to Evidence Map",
      "Teacher-confirmed judgments mirrored into Evidence Map Progress",
      "Raw field captures mirrored into Field Evidence with exact-revision receipts",
      "Approved rubric versions mirrored into Rubric Library"
    ],
    "raw_field_evidence_mirror_implemented": true,
    "field_evidence_mirror": {
      "status": "DEPLOYED \u2014 existing five-minute timer verified 2026-09-24 20:46:13 Atlantic",
      "action": "field-events",
      "method": "GET",
      "reader_role": "google",
      "filter": "course_id and source=field",
      "requirements": "Bounded pagination, stable event/revision IDs, explicit conflict state and completion signal",
      "apps_script_job": "runConnectedEvidenceSync",
      "sheet_tab": "Field Evidence",
      "upsert_key": [
        "course_id",
        "event_id"
      ],
      "no_echo": "Never write this mirror into Forms intake or Evidence_Log",
      "delivery_status": "Event API acceptance proves Netlify storage only; Google delivery needs a verified mirror receipt/readback"
    },
    "verification_limit": "Live scheduled run had 50 roster memberships, 0 judgments and 0 field captures; fictional capture round trip was verified in isolated preview, and the actual mirror source was tested locally."
  },
  "reporting_handoff": {
    "review_csv_scope": "course_outcome_judgment",
    "powerteacher_score_import_scope": "existing_assignment",
    "automatic_assignment_creation": false,
    "separate_standards_and_comments_csv_import_verified": false,
    "actual_score_template_required": true,
    "guide": "POWERSCHOOL_HANDOFF.md"
  },
  "requested_field_app_extensions": {
    "status": "CURRENT IMPLEMENTATION \u2014 see per-feature limits",
    "new_rubrics": {
      "implemented": true,
      "workflow": "Create or duplicate locally, review, then explicitly publish a shared immutable version; Gemini drafting remains unavailable until configured.",
      "existing_action_limit": "rubric approves a catalogue definition; rubric-publish creates a teacher-authored release under a distinct ID.",
      "implemented_operations": [
        "local draft authoring",
        "explicit immutable version publish",
        "shared approved library read",
        "Google approved-rubric mirror"
      ],
      "remaining_operations": [
        "shared draft save/read"
      ]
    },
    "completed_rubrics": {
      "implemented": true,
      "workflow": "Pinned rubric version; partial criterion records and grouped assessment_session_id; one atomic local session save; no automatic grade.",
      "grade_rule": "Blank criteria are unassessed; no automatic total or course grade.",
      "persisted_field": "assessment_session_id"
    },
    "voice_notes": {
      "implemented": "partial",
      "new_persisted_fields_required": [
        "voice_note_id",
        "transcript_text",
        "transcript_reviewed"
      ],
      "storage_rule": "Audio bytes remain outside Sheet cells and the JSON event endpoint; local blob URLs are not permanent links.",
      "implemented_operations": [
        "explicit local recording/playback",
        "device-local IndexedDB blobs",
        "reviewed transcript event fields",
        "protected transcription adapter (Gemini not configured)"
      ],
      "remaining_operations": [
        "protected cross-device upload/download",
        "cloud retention/deletion",
        "physical iPad microphone check"
      ]
    },
    "gemini_assistance": {
      "implemented": "adapter deployed; live AI unavailable until configured",
      "server_secrets": [
        "GEMINI_API_KEY"
      ],
      "server_configuration": [
        "GEMINI_MODEL"
      ],
      "actions": [
        "improve wording",
        "make concise",
        "draft feedback",
        "suggest next question",
        "draft reporting comment",
        "draft rubric descriptors",
        "transcribe selected voice note"
      ],
      "new_persisted_field_required": "comment_draft_id",
      "authority": "Original notes preserved; AI output remains a draft until teacher accepts/edits it; no automatic outcome judgment or PowerSchool submission.",
      "routes": [
        "/api/ai/assist",
        "/api/ai/transcribe",
        "/api/ai/rubric"
      ]
    },
    "compatibility": "Five optional fields are validated, persisted and read back: assessment_session_id, voice_note_id, transcript_text, transcript_reviewed, comment_draft_id. Existing events without these fields retain their historical revision hashes. Transcript text requires teacher review."
  },
  "implementation_updated_at": "2026-09-24",
  "current_status_reference": "FIELD_CONNECTION_STATUS.md"
}
