Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c3e680f
chore(worker): GITHUB_CLIENT_ID out of wrangler.toml — deploy-time se…
OriNachum Jul 11, 2026
34ce56e
feat(worker): D1 consent schema + db helpers (t2)
OriNachum Jul 11, 2026
83c0dd1
merge(t2): D1 consents schema + db helpers (getConsent/recordConsent/…
OriNachum Jul 11, 2026
6ad14b9
feat(t1): versioned Terms of Use + Privacy Policy pages with shared T…
OriNachum Jul 11, 2026
4c13456
merge(t1): Terms of Use + Privacy Policy pages, TERMS_VERSION single …
OriNachum Jul 11, 2026
750d9a6
feat(contract): add pick-the-right-word cloze exercises (t3, c16/h8)
OriNachum Jul 11, 2026
46be866
merge(t3): cloze pick-the-right-word exercise kind — contract, doctor…
OriNachum Jul 11, 2026
f414826
feat(infra): voice-bridge serverless spike + SAM template — Nova Soni…
OriNachum Jul 11, 2026
cdac077
merge(t4): voice-bridge SAM template + Lambda relay + spike verdict (…
OriNachum Jul 11, 2026
b384ec0
feat(worker): consent gate on both sign-in paths — pending-consent se…
OriNachum Jul 11, 2026
4dac569
merge(t5): consent gate on both sign-in paths — pending-consent sessi…
OriNachum Jul 11, 2026
3e64d7b
feat(t14): re-export content from released subjects — french 0.6.0, s…
OriNachum Jul 11, 2026
760d660
feat(worker): re-consent on TERMS_VERSION bump — stale-consent D1 che…
OriNachum Jul 11, 2026
5ed2384
merge(t6): re-consent on TERMS_VERSION bump — D1-backed stale-consent…
OriNachum Jul 11, 2026
aefb03c
feat(site): consent UI page wired to the pending-consent API (t10)
OriNachum Jul 11, 2026
8067e22
fix(worker): deterministic getConsent on granted_at ties — rowid tieb…
OriNachum Jul 11, 2026
a5842cf
merge(t10): consent UI page — five-state client flow against the pend…
OriNachum Jul 11, 2026
7a464af
feat(worker): self-serve export + delete — consent withdrawal = whole…
OriNachum Jul 11, 2026
7940c41
merge(t7): self-serve export + delete — GET /api/export, POST /api/de…
OriNachum Jul 11, 2026
8c1d688
feat(t8): roles + visibility — ADMIN_GITHUB_IDS allow-list, admin lea…
OriNachum Jul 11, 2026
768e974
merge(t8): roles + visibility — ADMIN_GITHUB_IDS allow-list, admin li…
OriNachum Jul 11, 2026
5885c4c
feat(t9): the approval gate — signed-out < signed-in < consented < AP…
OriNachum Jul 11, 2026
c0e0467
merge(t9): approval gate — approved flag, admin approve/revoke, four-…
OriNachum Jul 11, 2026
59d04ca
feat(t16): voice session end-to-end — approval-gated token mint, mont…
OriNachum Jul 11, 2026
cc37cd8
merge(t16): voice session e2e — /api/voice/token mint (cross-language…
OriNachum Jul 11, 2026
4764d72
feat(t15): Nova Pro tutoring through the existing broker — Converse w…
OriNachum Jul 11, 2026
d2c48bd
merge(t15): Nova Pro tutoring — Converse-direct config, tutor panel (…
OriNachum Jul 11, 2026
7339f1d
feat(t17): extended launch gate — consent/approval/deletion/tutor/voi…
OriNachum Jul 11, 2026
0f86e2e
Merge branch 'agent/c1-t17' into feat/consent-tutoring-uplift
OriNachum Jul 11, 2026
6e95af2
chore: bump 0.6.0, changelog, and record the c24 Converse correction …
OriNachum Jul 11, 2026
5dc6927
chore: gitignore .devague/questions (devague working-state, auto-added)
OriNachum Jul 11, 2026
2a1a244
chore: scope GitGuardian ignore to the t16 voice-token test fixture
OriNachum Jul 11, 2026
1280d0b
spec: apply the user-confirmed c24 correction (OpenAI-compat -> nativ…
OriNachum Jul 11, 2026
fc352ac
fix: reduce cognitive complexity of cloze conformance checks (S3776)
OriNachum Jul 11, 2026
cb11a5f
fix(voice-bridge): Query the concurrency gate instead of Scan-ing the…
OriNachum Jul 11, 2026
14aec2d
Merge branch 'fix/review-conformance' into feat/consent-tutoring-uplift
OriNachum Jul 11, 2026
b36e7d5
Merge branch 'fix/review-voice-bridge' into feat/consent-tutoring-uplift
OriNachum Jul 11, 2026
b239988
fix(worker): close two Qodo review findings — delete session leak + v…
OriNachum Jul 11, 2026
c926d27
Merge branch 'fix/review-worker' into feat/consent-tutoring-uplift
OriNachum Jul 11, 2026
4560221
docs: log the four Qodo/Sonar review fixes under 0.6.0
OriNachum Jul 11, 2026
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
20 changes: 20 additions & 0 deletions .gitguardian.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# GitGuardian secret-scan configuration.
#
# The single ignored path below is a TEST-ONLY fixture, not a leaked
# credential. It pins the cross-language voice-token contract (task t16): a
# voice token minted ONCE by workers/learn-api/src/voice.js#mintVoiceToken with
# a deterministic, fake secret ("cross-language-contract-test-secret") over a
# public payload, committed so the JS minter (workers/learn-api/test/
# voice.test.js) and the Python verifier (tests/test_voice_token_cross_language
# .py against infra/voice_bridge/tokens.py) can each prove they still agree
# byte-for-byte. The token is an HMAC of non-secret data with a non-secret test
# key — GitGuardian's JWT detector matches its shape, but there is no real
# secret here (the fixture's own `note` says as much), and it CANNOT be changed
# without breaking the contract pin. Scope the ignore to this one file only.
version: 2
secret:
ignored-paths:
- tests/fixtures/voice_token_cross_language.json
ignored-matches:
- name: voice-token cross-language contract test secret (t16 fixture)
match: cross-language-contract-test-secret
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,5 @@ skills.local.yaml

# wrangler local state (created by `wrangler pages deploy` at repo root)
.wrangler/

.devague/questions/
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.6.0] - 2026-07-11

### Added

- Consent-first sign-in: both the web callback and device-flow paths now issue a short-lived pending-consent session and write ZERO to D1 until the learner accepts the published Terms/Privacy — the consent row is recorded before the learner row (consents table, no FK to learners).
- Versioned re-consent: a TERMS_VERSION bump routes a live full session back to the consent screen (403 consent_required, reason stale_version) until re-acceptance.
- Self-serve data rights: GET /api/export (whole-learner JSON across every subject + consent history) and POST /api/delete (confirm = own github_user_id) erasing all three tables and revoking the session.
- Role-aware access: ADMIN_GITHUB_IDS server-side allow-list, GET /api/admin/learners roster, default-private learner visibility with POST /api/me/visibility.
- Approval-gated Bedrock tutoring tier: POST /api/admin/approve|revoke, a four-level tutor gate (signed-out < consented < approved < configured) where a non-approved /api/tutor call 403s with zero outbound inference; approve is c20-gated on current consent.
- Nova Pro text tutoring surface (grading, adaptive next-step, personalized cloze-story generation) through the existing broker via Bedrock Converse — config only, no provider SDK added to the Worker.
- Nova Sonic 2 voice: approval-gated POST /api/voice/token mint with a per-learner monthly budget, the /learn/voice page, and a serverless SAM voice bridge (API Gateway WebSocket + arm64 Lambda + $20 AWS Budgets ceiling) under infra/.
- Terms of Use + Privacy Policy pages under /learn, versioned from a single shared/terms-version.mjs source, naming GitHub, Cloudflare, and AWS Bedrock as processors; a /learn/consent page with a five-state client flow.
- Cloze (pick-the-right-word) exercise kind in the subject-plugin contract (contract §3.6.1), shipped by french-cli, spanish-cli, and culture-guide and re-exported to /learn.
- Extended launch gate: tools/launch-gate/consent_walk.mjs drives the consent/approval/deletion/tutor/voice/cloze success signals end-to-end (LOCAL authed flows + LIVE unauthenticated probes), tests/test_launch_gate_invariants.py locks the boundary invariants (no email/password, no provider SDK, no forked subject prose), with a recorded pre-uplift baseline.

### Changed

- GITHUB_CLIENT_ID is no longer committed in wrangler.toml [vars] — it is set as a secret from .env GITHUB_APP_CLIENT_ID, so a plain wrangler deploy never overwrites it.
- check-static-auth.mjs now polices four per-script fetch whitelists (learner, consent, voice, tutor); the tutor surface renders only for admin-approved learners.

### Fixed

- Launch-gate api-server.mjs no longer throws ERR_HTTP_HEADERS_SENT on Set-Cookie responses (consent accept / login / logout / delete), so those flows can be exercised end-to-end.
- getConsent tiebreaks same-millisecond granted_at rows by rowid, removing a flaky re-consent race.
- Review fix (security): POST /api/delete now revokes EVERY session for the learner, not only the calling token — a per-uid revocation marker (`revoked_uid:<uid>`) that requireAuth checks, so "delete logs me out everywhere."
- Review fix (reliability): the monthly voice budget is now booked with a compare-and-swap retry on the learner row, so concurrent /api/voice/token mints can no longer both pass the cap check and exceed it.
- Review fix (performance): the voice-bridge Lambda's $connect concurrency gate queries session metadata under a constant partition key (Query COUNT) instead of a full-table Scan that read every frame item.
- Review fix (maintainability): the two new cloze conformance validators were refactored below SonarCloud's cognitive-complexity threshold (behavior-preserving helper extraction).

## [0.5.4] - 2026-07-11

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
- honesty: All three subjects pass learn subject doctor with at least one cloze item present, and a learner's pre-existing mastery/progress reads back identically after the re-export (item_id join keys unchanged)
- Terms of Use + Privacy Policy (#11): both pages published, versioned, linked from the /learn footer and from the consent notice. The Privacy Policy names GitHub (OAuth identity), Cloudflare (Workers/KV/D1/Pages hosting), and AWS Bedrock (Nova Sonic 2 / Nova Pro — approved tier only, learner speech/text leaves to the model provider) as processors, states retention + the delete/export path, and matches what the code actually persists (no email, no password).
- honesty: Every processor the code actually calls (GitHub OAuth endpoints, Cloudflare storage/hosting, the Bedrock-backed inference endpoint) is named in the published Privacy Policy, and nothing the policy claims about data (no email, no password, deletable on request) is contradicted by the schema or worker code
- Nova Pro tutoring (#8): text tutoring (exercise grading, adaptive next-step, explanation, personalized cloze-story generation) routes through the existing /api/tutor broker with INFERENCE_URL pointing directly at AWS Bedrock's OpenAI-compatible chat-completions endpoint (bedrock-runtime .../openai/v1) and a Bedrock API key as INFERENCE_TOKEN — AWS Bedrock is the only service that serves AWS Nova models; no sibling-hosted model server, no idle host. Cost-when-busy: per-token Nova Pro spend only. Generated cloze stories and their results are recorded to the existing ledger as contract-valid records with stable item_ids.
- instruction: Verify with a curl against the Bedrock OpenAI-compatible endpoint using the Bedrock API key (expect a Nova Pro completion), then the worker test that /api/tutor forwards approved traffic and the Worker diff shows no provider SDK added
- honesty: The Worker gains no Bedrock/provider SDK and no code change beyond config — a Bedrock API key against the OpenAI-compatible endpoint returns a Nova Pro completion, and the broker forwards approved traffic to it unchanged (h6's condition, retargeted at Bedrock-direct)
- Nova Pro tutoring (#8): text tutoring (exercise grading, adaptive next-step, explanation, personalized cloze-story generation) routes through the existing /api/tutor broker with INFERENCE_URL pointing directly at AWS Bedrock's native Converse API (bedrock-runtime .../model/us.amazon.nova-pro-v1:0/converse) and a Bedrock API key as INFERENCE_TOKEN — AWS Bedrock is the only service that serves AWS Nova models; no sibling-hosted model server, no idle host. Cost-when-busy: per-token Nova Pro spend only. Generated cloze stories and their results are recorded to the existing ledger as contract-valid records with stable item_ids. (Corrected 2026-07-11, user-confirmed: Bedrock's OpenAI-compatible endpoint does NOT serve Nova Pro — see the correction note below.)
- instruction: Verify with a curl against the Bedrock Converse endpoint using the Bedrock API key (expect a Nova Pro completion), then the worker test that /api/tutor forwards approved traffic and the Worker diff shows no provider SDK added
- honesty: The Worker gains no Bedrock/provider SDK and no code change beyond config — a Bedrock API key against the Converse endpoint returns a Nova Pro completion, and the broker forwards approved traffic to it unchanged (h6's condition, retargeted at Bedrock-direct)

## Honesty conditions

Expand Down Expand Up @@ -66,6 +66,31 @@
- Consent UX shape: an unconsented sign-in issues a short-lived pending-consent session whose ONLY capabilities are viewing the consent notice + accepting or declining — no learner row exists yet, /api/me reports pending_consent, every other authed route 403s. Decline = the session is dropped and nothing was ever written. This keeps the OAuth redirect flow intact (the user lands signed-in-pending on the consent page) without persisting pre-consent.
- Sequencing: the consent gate + published policy docs (#10/#11) ship BEFORE the tutoring tier is enabled for anyone — no learner is approved for Bedrock until the Privacy Policy disclosing Bedrock processing is live and they have consented to it. Content work (#9) proceeds upstream in parallel, unblocked.
- Content flows upstream-first (#9): curriculum improvements are authored in french-cli / spanish-cli / culture-guide, released to PyPI, then pulled into /learn via learn site export + redeploy — learn-cli never forks subject content locally
- Model access is Bedrock-direct (user decision): AWS Bedrock is the only service that can serve AWS Nova models — both the Nova Pro text path (OpenAI-compatible endpoint + Bedrock API key) and the Nova Sonic 2 voice path (InvokeModelWithBidirectionalStream) terminate at Bedrock itself. Anything between the learner and Bedrock is thin credentialed plumbing enforcing the approval gate, never a model host. Supersedes the earlier cloudai-cli/ec2bedrock-cli serving framing.
- Model access is Bedrock-direct (user decision): AWS Bedrock is the only service that can serve AWS Nova models — both the Nova Pro text path (native Converse API + Bedrock API key; corrected 2026-07-11 from "OpenAI-compatible endpoint", which does not serve Nova Pro) and the Nova Sonic 2 voice path (InvokeModelWithBidirectionalStream) terminate at Bedrock itself. Anything between the learner and Bedrock is thin credentialed plumbing enforcing the approval gate, never a model host. Supersedes the earlier cloudai-cli/ec2bedrock-cli serving framing.
- Policy docs live /learn-local (user decision, resolves v1): learn-cli authors, versions, and serves the Terms of Use + Privacy Policy under /learn — it is the only surface processing personal data (auth, ledger, Bedrock); org's static site collects nothing. Re-consent versioning stays inside learn-cli, uncoupled from org releases. org gets a filed issue to link the policies site-wide and to record this decision.
- AWS plumbing is serverless, pay-as-you-go (user decision, resolves v7): no EC2 unless explicitly requested. The Nova Sonic 2 voice bridge follows league-of-agents-platform's proven infra pattern (built by Fable): AWS SAM, Lambda (arm64) behind an API Gateway WebSocket API relaying learner audio to Bedrock's bidirectional stream, an AWS Budgets alarm pinned to a hard monthly USD ceiling with every sizing choice commented against it, and zero idle cost. Bedrock itself is serverless GenAI — nothing always-on sits between the learner and it.

## Post-convergence correction (2026-07-11, user-confirmed)

> **Confirmed by the user on 2026-07-11** (frame question `q1`, resolved). The
> correction below is now folded into c24 and the "Model access is
> Bedrock-direct" decision above; this note is retained as the evidence trail.

- **c24 endpoint wording (OpenAI-compatible → native Converse).** c24 (and the
Decisions "Model access is Bedrock-direct" line) say the Nova Pro text path
points `INFERENCE_URL` at Bedrock's *OpenAI-compatible chat-completions*
endpoint (`bedrock-runtime.<region>.amazonaws.com/openai/v1/...`). A live probe
during t15 (2026-07-11) proved that endpoint returns `model_not_found` for
Nova Pro in every region tried (us-east-1, us-west-2, eu-west-1, eu-central-1),
and `/openai/v1/models` is not even an operation. The **native Bedrock Converse
API** — `POST /model/us.amazon.nova-pro-v1:0/converse` with a Bedrock API key as
the Bearer token — DID return real Nova Pro completions (HTTP 200, ~0.5–1s) and
tolerates the broker's `learner` stamp as an extra field. So the honesty
condition **h11 still holds** (no Worker code change beyond config; no provider
SDK): only the endpoint URL and the request/response *shape* change from the
OpenAI chat-completions form to the Converse form. The shipped code
(`wrangler.toml`, `workers/learn-api/README.md`, `site-astro/src/scripts/
tutor-core.js`) already targets Converse; this note reconciles the spec's
wording with what shipped. **Proposed:** retarget c24's "OpenAI-compatible
chat-completions endpoint" wording to the native Converse API. Awaiting user
confirmation.
Loading
Loading