Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .codex/skills/software-engineer/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
name: software-engineer
description: Implement and investigate software features and bugfixes for a senior full-stack engineering workflow with Gleam, Erlang/OTP, web technologies, and LTI/LMS integrations. Use when asked to debug, investigate, implement, or ship product changes; for larger features, drive execution from docs/features/<feature>/prd.md, fdd.md, and plan.md.
---

# Software Engineer

## Overview

Execute pragmatic, production-quality feature and bugfix work across backend, frontend, and integration boundaries.

## Core Workflow

1. Gather context.

- Read the prompt, related code paths, tests, and recent diffs.
- Identify affected domains: Gleam/Erlang service logic, web UI/API contracts, and LTI/LMS integration points.

2. Classify the work size.

- Use `investigation` for root-cause analysis or uncertain requirements.
- Use `small-change` for isolated bugfixes or narrowly scoped feature updates.
- Use `feature-delivery` for larger functionality that should follow a feature spec.

3. Select execution path.

- `investigation`: Reproduce, isolate, form hypothesis, validate with evidence, propose minimal fix.
- `small-change`: Implement smallest safe patch, preserve behavior outside scope, add/update tests.
- `feature-delivery`: Read spec files in order:
1. `docs/features/<feature>/prd.md`
2. `docs/features/<feature>/fdd.md`
3. `docs/features/<feature>/plan.md`
Treat `prd.md` as product source of truth, `fdd.md` as design constraints, `plan.md` as execution sequence.

4. Implement with stack discipline.

- Keep OTP boundaries explicit: processes, supervision, message flow, failure handling, and timeouts.
- Keep Gleam types and module interfaces clear; prefer small, composable functions.
- Preserve backward compatibility for web contracts unless the spec explicitly changes them.
- For LMS/LTI work, enforce correctness for launch/auth flows, claims, roles, and assignment/grade data handling.

5. Verify before handoff.

- Run the narrowest relevant tests first, then broader suites as needed.
- Validate error paths, edge cases, and integration assumptions.
- Confirm docs/config/migrations are updated when behavior changes.

6. Report clearly.

- State what changed, why, and what was validated.
- List assumptions, risks, and follow-up tasks if scope was constrained.

## Delivery Rules

- Prefer small, reviewable commits and minimal blast radius.
- Avoid speculative refactors unless required to complete the task safely.
- Preserve existing design-system and architecture conventions.
- Escalate contradictions between spec files; do not silently guess.

## References

- Use [references/execution-checklists.md](references/execution-checklists.md) for repeatable checklists.
4 changes: 4 additions & 0 deletions .codex/skills/software-engineer/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "Software Engineer"
short_description: "Ship features and bugfixes across Gleam, OTP, web, and LTI"
default_prompt: "Use $software-engineer to investigate or implement a bugfix/feature, and for larger work follow docs/features/<feature>/prd.md, fdd.md, and plan.md."
33 changes: 33 additions & 0 deletions .codex/skills/software-engineer/references/execution-checklists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Execution Checklists

## Investigation Checklist

- Reproduce issue with concrete inputs.
- Capture expected vs actual behavior.
- Narrow fault domain (backend, frontend, integration, data).
- Prove or disprove top hypotheses with evidence.
- Identify minimal safe fix and regression risks.

## Small Change Checklist

- Confirm scope boundaries and non-goals.
- Update only required modules/routes/components.
- Add or update focused tests.
- Validate public interfaces and backward compatibility.
- Verify logs/errors/telemetry remain actionable.

## Feature Delivery Checklist

- Read `prd.md`, `fdd.md`, and `plan.md` in order.
- Map requirements to implementation units and tests.
- Implement in plan order unless blockers force resequencing.
- Validate acceptance criteria from the PRD.
- Verify UX/API/integration behavior end-to-end.

## LTI/LMS Checklist

- Validate launch and auth flow assumptions.
- Confirm required claims/roles/context fields are handled.
- Check course/user identifiers and tenancy boundaries.
- Verify grade/assignment semantics where applicable.
- Confirm failure modes return clear, supportable errors.
2 changes: 1 addition & 1 deletion .github/workflows/publish-hex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
uses: erlef/setup-beam@v1.15.4
with:
otp-version: "27.1.2"
gleam-version: "1.10.0"
gleam-version: "1.14.0"
rebar3-version: "3"
elixir-version: "1.18.3"

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ jobs:
- uses: erlef/setup-beam@v1
with:
otp-version: "27.1.2"
gleam-version: "1.10.0"
gleam-version: "1.14.0"
rebar3-version: "3"
elixir-version: "1.18.3"
- run: gleam deps download
- run: scripts/lint_conformance_matrix.sh
- run: gleam test
- run: gleam format --check src test
66 changes: 66 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# AGENTS

## Project Scope

- This file defines local agent guidance for the `lightbulb` repository.

## Project Overview

- `lightbulb` is a Gleam library for building LTI 1.3 tools.
- Core responsibilities include OIDC login, launch validation, and LTI service integrations.
- Current service coverage includes AGS (Assignments and Grades), NRPS (Names and Roles), and Deep Linking.
- Provider interfaces are designed to keep storage and HTTP concerns pluggable.

## Architecture

- Entry module:
- `src/lightbulb.gleam` re-exports primary APIs for consumers.
- Launch/auth core:
- `src/lightbulb/tool.gleam` handles OIDC login, JWT verification, claim validation, and message-type routing.
- Feature modules:
- `src/lightbulb/services/access_token.gleam` builds OAuth client assertions and fetches service tokens.
- `src/lightbulb/services/ags*.gleam` implements AGS line item and score workflows.
- `src/lightbulb/services/nrps*.gleam` implements membership retrieval.
- `src/lightbulb/deep_linking*.gleam` handles deep-link settings, content items, response JWT, and form-post helper.
- Provider boundary:
- `src/lightbulb/providers/data_provider.gleam` defines persistence/key/registration interfaces.
- `src/lightbulb/providers/http_provider.gleam` abstracts HTTP transport.
- `src/lightbulb/providers/memory_provider.gleam` offers in-memory development/testing storage.
- Crypto/key utilities:
- `src/lightbulb/jose.gleam` and `src/lightbulb/jwk.gleam` wrap JOSE/JWK operations.
- Tests:
- `test/lightbulb/**` contains unit and integration-style tests grouped by domain.

## Engineering Workflow

- Keep changes minimal and scoped to the feature/bug request.
- Prefer additive changes over broad refactors unless refactoring is required for correctness.
- Run focused tests first, then broader tests when behavior changes cross module boundaries.
- Prefer explicit `Result` error paths over exceptions or panics.
- When working from a feature `plan.md`, check off checklist items when tasks are completed.
- For any public API or client-facing behavior change, update `CHANGELOG.md` in the same change:
- add/update notes under `Unreleased` (WIP) until release day
- note the intended target version
- summarize what changed for consumers
- include concrete migration steps/code changes required in client apps
- when cutting a release, promote `Unreleased` notes into the released version section

## Gleam Conventions

- Prefer explicit, typed decoding and structured error types for public APIs.
- Reuse existing modules and patterns before introducing new helpers.
- For boolean early-return checks, prefer `gleam/bool.guard`.
- Prefer returning `Result(_, ErrorType)` with explicit error variants rather than raw strings.
- When string errors are needed for compatibility, provide dedicated conversion helpers
(for example `lightbulb/errors.*_to_string`) instead of ad-hoc string literals.

Preferred pattern:

```gleam
use <- bool.guard(
when: some_condition,
return: Error("some.error.code"),
)
```

- Avoid creating custom `bool_guard` wrappers when `bool.guard` is sufficient.
162 changes: 162 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Changelog

This changelog serves as release notes and is maintained as WIP until a release is cut.

## [Unreleased] (target: 2.0.0)

### Features

- Full LTI 1.3 Core launch support with required claim validation, audience/`azp`
handling, nonce hardening, and state/target-link consistency enforcement.
- Deep Linking support for decoding deep-link launches and building signed response
JWT/form-post payloads.
- Service integrations for AGS and NRPS workflows.
- OAuth service token typed API and error taxonomy:
- `services/access_token.fetch_access_token/3`
- `services/access_token.fetch_access_token_with_options/4`
- `services/access_token.access_token_error_to_string/1`
- Optional OAuth token cache helper:
- `services/access_token_cache` with cache keying, staleness checks, and
`fetch_access_token_with_cache/4`.
- Replaced custom internal logger FFI implementation with the `logging` (`v1.3.0`)
package while preserving `lightbulb/utils/logger` call sites.
- AGS API expansion and hardening (target: `2.0.0`):
- New typed AGS errors and string conversion helper:
`services/ags.AgsError`, `services/ags.ags_error_to_string/1`
- Full line item service coverage:
`get_line_item/3`, `list_line_items/4`, `update_line_item/3`,
`delete_line_item/3`, `create_line_item/6`,
`fetch_or_create_line_item/7`
- Results service support:
`list_results/4` with `ResultsQuery`
- Paging metadata support:
`http/link_header` parser and `services/ags.Paged(a)` responses
- Scope helpers and guards:
`can_*` and `require_can_*` predicates for line items/scores/results
- Added AGS readonly line-item scope constant:
`lineitem_readonly_scope_url`
- NRPS API expansion and hardening (target: `2.0.0`):
- New typed NRPS errors and conversion helper:
`services/nrps.NrpsError`, `services/nrps.nrps_error_to_string/1`
- Spec-aligned NRPS claim decoding:
`services/nrps.get_nrps_claim/1` now requires
`context_memberships_url` and `service_versions`
- Scope helpers:
`can_read_memberships/1`, `require_can_read_memberships/1`
- Options-based membership fetch and paging support:
`MembershipsQuery`, `MembershipsPage`,
`fetch_memberships_with_options/4`,
`fetch_next_memberships_page/3`,
`fetch_differences_memberships_page/3`
- Compatibility wrapper retained:
`fetch_memberships/3` still returns `List(Membership)` via default options.
- Certification governance package (target: `2.0.0`):
- Added schema-driven conformance matrix:
`docs/complete_lti_support/conformance_matrix.md`
- Added certification runbook:
`docs/complete_lti_support/certification_runbook.md`
- Added evidence package layout and dry-run records under:
`docs/complete_lti_support/evidence/`
- Added matrix lint automation:
`scripts/lint_conformance_matrix.sh`
- Added conformance-focused test modules under:
`test/lightbulb/conformance/`
- CI now enforces matrix lint in `.github/workflows/test.yml`.

### Bug Fixes

- Core launch validation now requires persisted login context and enforces state and
`target_link_uri` consistency between OIDC login and launch validation.
- Core audience handling now supports `aud` as string or list and enforces `azp`
semantics for multi-audience tokens.
- OAuth token decode now tolerates missing `scope` and `expires_in` fields while
preserving usable defaults.
- Non-2xx OAuth token responses now parse RFC 6749-style error bodies
(`error`, `error_description`, `error_uri`) before falling back to generic status
errors.
- OAuth client assertion defaults are hardened with short lifetime defaults (5 minutes)
and configurable audience selection.
- Replaced the `birl` dependency with direct use of `gleam/time` (`duration` and
`timestamp`) for nonce/login-context expiry and JWT timestamp handling.
- AGS score posting now accepts `200/201/202/204` success statuses and no longer
sends the line-item-container `Accept` header for score POST requests.
- `grade_passback_available/1` now reflects score-posting capability (`scope/score`)
instead of results-read capability.
- NRPS membership decode now tolerates minimal valid member payloads and no longer
requires non-normative claim fields (`errors`, `validation_context`).

### Breaking Changes

- `DataProvider` implementations must add:
- `save_login_context(LoginContext) -> Result(Nil, LaunchContextError)`
- `get_login_context(String) -> Result(LoginContext, LaunchContextError)`
- `consume_login_context(String) -> Result(Nil, LaunchContextError)`
- Removed provider composition helper types/functions from public API:
- `providers/data_provider.LaunchContextProvider`
- `providers/data_provider.from_parts/6`
- Migration for provider implementations:
- Construct `DataProvider(...)` directly instead of calling `from_parts/6`.
- `DataProvider` provider operations now use typed provider errors:
- `create_nonce() -> Result(Nonce, ProviderError)`
- `get_registration(String, String) -> Result(Registration, ProviderError)`
- `get_deployment(String, String, String) -> Result(Deployment, ProviderError)`
- `get_active_jwk() -> Result(Jwk, ProviderError)`
- Core APIs now return structured error types instead of string codes:
- `tool.oidc_login/2 -> Result(#(String, String), CoreError)`
- `tool.validate_launch/3 -> Result(Claims, CoreError)`
- `tool.validate_message_type/1 -> Result(Claims, CoreError)`
- Deep Linking APIs now return `DeepLinkingError` instead of string error IDs:
- `deep_linking.get_deep_linking_settings/1`
- `deep_linking.build_response_jwt/5`
- `deep_linking.build_response_form_post/2`
- `deep_linking/content_item.validate_items/2`
- `DataProvider.validate_nonce/1` now returns `Result(Nil, NonceError)` and must map
nonce outcomes to explicit variants (`NonceInvalid`, `NonceExpired`, `NonceReplayed`).
- Clients relying on string matching should migrate to variant matching, or explicitly
convert to short user-facing messages via `lightbulb/errors.{core_error_to_string}`
and `lightbulb/errors.{nonce_error_to_string}`.
- For deep-linking user-facing messages, use
`lightbulb/errors.{deep_linking_error_to_string}`.
- AGS `LineItem` constructor now includes additional optional AGS fields:
`resource_link_id`, `tag`, `start_date_time`, `end_date_time`,
`grades_released`.
- Migration for OAuth callers:
- `fetch_access_token/3` now returns typed errors:
`Result(AccessToken, AccessTokenError)`.
- If you need user-facing string messages, convert with
`access_token_error_to_string/1` at process boundaries.
- If you need custom assertion audience or TTL, switch to
`fetch_access_token_with_options/4`.
- To reduce token endpoint traffic, adopt `services/access_token_cache` and route
service-token fetches through `fetch_access_token_with_cache/4`.
- Migration for AGS callers:
- AGS APIs return typed errors (`AgsError`) and no longer provide
string-error compatibility wrappers.
- Pattern-match on `AgsError`, or convert with `ags_error_to_string/1`
at process boundaries.
- If you construct `LineItem` directly, add the new optional fields with
`option.None` unless needed.
- Migration for NRPS callers:
- `fetch_memberships/3` now returns `Result(List(Membership), NrpsError)`
(typed errors instead of string errors).
- Pattern-match on `NrpsError`, or convert at process boundaries with
`nrps_error_to_string/1`.
- If you construct `Membership` directly, use the new shape:
required: `user_id`, `roles`; optional: `status`, `name`, `given_name`,
`family_name`, `middle_name`, `email`, `picture`, `lis_person_sourcedid`.
- For filtering/paging flows, move from `fetch_memberships/3` to
`fetch_memberships_with_options/4` and continuation helpers.

## 1.0.0

### Features

- Initial stable release of the Lightbulb LTI 1.3 library.

### Bug Fixes

- None noted.

### Breaking Changes

- None.
Loading