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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
48 changes: 26 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,33 @@ on:
push:
pull_request:

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest

runs-on: ubuntu-26.04
steps:
- uses: actions/checkout@v6

- uses: denoland/setup-deno@v2
- uses: actions/checkout@v7.0.1
- uses: actions/setup-node@v7.1.0
with:
deno-version: latest
cache: true

- name: Format check
run: deno task fmt:check

- name: Lint
run: deno task lint

- name: Type check
run: deno task check

- name: Worker bundle check
run: deno task check:worker

- name: Test
run: deno task test
node-version-file: .node-version
cache: npm
- name: Install npm
run: npm install --global npm@12.2.1
- name: Install dependencies
run: npm ci
- name: Configure browser caches
run: |
echo "PUPPETEER_CACHE_DIR=$RUNNER_TEMP/lantern-browser" >> "$GITHUB_ENV"
echo "XDG_CACHE_HOME=$RUNNER_TEMP/lantern-worker-cache" >> "$GITHUB_ENV"
- name: Enable Chrome sandbox user namespaces
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Install Chrome from a clean cache
timeout-minutes: 5
run: npm run browser:install
- name: Audit dependencies
run: npm audit --audit-level=high
- name: Build and validate both Workers and the Python container
timeout-minutes: 10
run: npm run validate
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ var/authoring-drafts/
.wrangler/
output/
.planning
node_modules/

.cloudflare/
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
26.13.0
3 changes: 3 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@jsr:registry=https://npm.jsr.io
save-exact=true
@std:registry=https://npm.jsr.io
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Rules:

Default stack:

- Deno
- Node.js 26 LTS and TypeScript 7
- Hono
- Cloudflare D1 for persistence
- Cloudflare R2 for reviewed artifacts and evidence
Expand Down Expand Up @@ -174,9 +174,9 @@ Prefer:

Before finishing TypeScript work, run:

- `deno fmt`
- `deno lint`
- `deno check`
- `npm run fmt`
- `npm run lint`
- `npm run check`

If tests exist for the touched area, run them.

Expand Down
32 changes: 16 additions & 16 deletions APP_PACKAGE_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,24 +341,24 @@ declare function readLocalState<T = unknown>(): Promise<T | null>;
```ts
type AttemptEvent =
| {
type: 'answer';
questionId: string;
answer: string | string[];
correct?: boolean;
scoreGiven?: number;
scoreMaximum?: number;
timestamp: string;
}
type: 'answer';
questionId: string;
answer: string | string[];
correct?: boolean;
scoreGiven?: number;
scoreMaximum?: number;
timestamp: string;
}
| {
type: 'progress';
checkpoint: string;
value: number;
timestamp: string;
}
type: 'progress';
checkpoint: string;
value: number;
timestamp: string;
}
| {
type: 'complete';
timestamp: string;
};
type: 'complete';
timestamp: string;
};

declare function emitAttemptEvent(event: AttemptEvent): Promise<void>;
declare function finalizeAttempt(input: {
Expand Down
38 changes: 19 additions & 19 deletions AUTHORING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,28 @@ Do not start by reading internal routes or admin code.
List the curated starters:

```sh
deno task app:new --list-starters
npm run app:new -- --list-starters
```

If you are building a general reviewed activity, scaffold `simple-activity`:

```sh
deno task app:new /tmp/my-lantern-app --starter=simple-activity --app-id=my-app --title="My App"
npm run app:new -- /tmp/my-lantern-app --starter=simple-activity --app-id=my-app --title="My App"
```

If you are building a reviewed browser autograder, scaffold
`browser-autograder`:

```sh
deno task app:new /tmp/my-browser-autograder --starter=browser-autograder --app-id=my-autograder --title="My Autograder"
npm run app:new -- /tmp/my-browser-autograder --starter=browser-autograder --app-id=my-autograder --title="My Autograder"
```

Then run the same loop for either starter:

```sh
deno task app:validate /tmp/my-browser-autograder
deno task app:test-preview /tmp/my-browser-autograder
deno task app:preview /tmp/my-browser-autograder
npm run app:validate -- /tmp/my-browser-autograder
npm run app:test-preview -- /tmp/my-browser-autograder
npm run app:preview -- /tmp/my-browser-autograder
```

Use `app:preview` when you want a live browser URL. `app:test-preview` is the
Expand All @@ -54,12 +54,12 @@ If you also want Lantern's local admin and package review UI on your machine,
run:

```sh
deno task local:init
deno task local:bootstrap
deno task local:start
npm run local:init
npm run local:bootstrap
npm run local:start
```

Then open the localhost URL printed by Wrangler.
Then open the localhost URL printed by the Cloudflare Vite dev server.

## Package Shape

Expand Down Expand Up @@ -137,19 +137,19 @@ submission binding, finalize, grading, and audit for both paths.
List curated starters:

```sh
deno task app:new --list-starters
npm run app:new -- --list-starters
```

Scaffold one package:

```sh
deno task app:new /path/to/app --starter=simple-activity --app-id=my-app --title="My App"
npm run app:new -- /path/to/app --starter=simple-activity --app-id=my-app --title="My App"
```

Run the authoring dev loop:

```sh
deno task app:dev /path/to/app
npm run app:dev -- /path/to/app
```

`app:dev` serves a stable local preview URL, reruns validation and preview
Expand All @@ -159,25 +159,25 @@ next valid save reloads the preview.
Validate one package without serving a preview:

```sh
deno task app:validate /path/to/app
npm run app:validate -- /path/to/app
```

Execute preview assertions without serving a preview:

```sh
deno task app:test-preview /path/to/app
npm run app:test-preview -- /path/to/app
```

Start the runtime preview without the validation loop:

```sh
deno task app:preview /path/to/app
npm run app:preview -- /path/to/app
```

Choose a port explicitly:

```sh
deno task app:preview /path/to/app --port=8421
npm run app:preview -- /path/to/app --port=8421
```

What validation checks today:
Expand Down Expand Up @@ -226,7 +226,7 @@ Do not add:
- initial local state

`preview/tests.json` is a simple list of DOM assertions that describe what must
be visible in preview. Run `deno task app:test-preview /path/to/app` to execute
be visible in preview. Run `npm run app:test-preview -- /path/to/app` to execute
those assertions on Lantern's local preview seam. Keep the checks narrow:
selector existence, exact text, or contains text. This is a reviewed package
checklist, not a second browser automation product.
Expand All @@ -239,7 +239,7 @@ and admin import.
When you want to move a reviewed package into Lantern's governed inventory:

1. Start Lantern locally.
2. Open `/admin/packages/import` on the localhost URL printed by Wrangler.
2. Open `/admin/packages/import` on the localhost URL printed by the Cloudflare Vite dev server.
3. Choose the exact package directory you scaffolded and validated locally.

Lantern validates the manifest and referenced files, stores an immutable
Expand Down
14 changes: 7 additions & 7 deletions AUTHORING_FOR_LLMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ teacher-authored autograder.
These are the human-facing commands the generated package should align with:

```sh
deno task app:new /tmp/my-app --starter=simple-activity --app-id=my-app --title="My App"
deno task app:new /tmp/my-app --starter=browser-autograder --app-id=my-app --title="My App"
npm run app:new -- /tmp/my-app --starter=simple-activity --app-id=my-app --title="My App"
npm run app:new -- /tmp/my-app --starter=browser-autograder --app-id=my-app --title="My App"
```

## Required Output
Expand Down Expand Up @@ -219,7 +219,7 @@ Constraints:
- Include manifest.json, dist/index.html, dist/app.js, content/activity.json,
preview/fixtures.json, preview/tests.json, grading/specs/checks.spec.js, and
evidence/example-output.json
- Keep the package aligned with `deno task app:new /tmp/my-app --starter=browser-autograder --app-id=my-app --title="My App"`
- Keep the package aligned with `npm run app:new -- /tmp/my-app --starter=browser-autograder --app-id=my-app --title="My App"`
```

For a simpler reviewed activity, use the same constraints but match the
Expand All @@ -230,10 +230,10 @@ For a simpler reviewed activity, use the same constraints but match the
After generating the package, run:

```sh
deno task app:dev /path/to/app
deno task app:validate /path/to/app
deno task app:test-preview /path/to/app
deno task app:preview /path/to/app
npm run app:dev -- /path/to/app
npm run app:validate -- /path/to/app
npm run app:test-preview -- /path/to/app
npm run app:preview -- /path/to/app
```

Use `app:dev` for the normal edit loop: it keeps one preview URL open, reruns
Expand Down
16 changes: 8 additions & 8 deletions BROWSER_AUTOGRADER_COOKBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ model.
Start from the shipped browser-autograder starter:

```sh
deno task app:new /tmp/my-browser-autograder --starter=browser-autograder --app-id=my-autograder --title="My Autograder"
npm run app:new -- /tmp/my-browser-autograder --starter=browser-autograder --app-id=my-autograder --title="My Autograder"
```

If you only need a simpler activity with no reviewed browser grader specs, use
Expand Down Expand Up @@ -111,26 +111,26 @@ not a second submission model.
Validate the package:

```sh
deno task app:validate /tmp/my-browser-autograder
npm run app:validate -- /tmp/my-browser-autograder
```

Execute the preview assertions:

```sh
deno task app:test-preview /tmp/my-browser-autograder
npm run app:test-preview -- /tmp/my-browser-autograder
```

Open a live preview URL when you want to inspect the rendered package:

```sh
deno task app:preview /tmp/my-browser-autograder
npm run app:preview -- /tmp/my-browser-autograder
```

For a richer shipped example, these commands already work against `web-checkup`:

```sh
deno task app:validate examples/apps/web-checkup
deno task app:test-preview examples/apps/web-checkup
npm run app:validate -- examples/apps/web-checkup
npm run app:test-preview -- examples/apps/web-checkup
```

## Example: Web Checkup
Expand All @@ -150,8 +150,8 @@ checks instead of one starter file.

When the package is ready for governed review:

1. Start Lantern locally with `deno task local:init`,
`deno task local:bootstrap`, and `deno task local:start`.
1. Start Lantern locally with `npm run local:init`,
`npm run local:bootstrap`, and `npm run local:start`.
2. Open `/admin/packages/import` on the localhost URL printed by Wrangler.
3. Import the exact package directory you validated locally.

Expand Down
24 changes: 0 additions & 24 deletions Dockerfile

This file was deleted.

9 changes: 9 additions & 0 deletions Dockerfile.python-grader
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3.15-slim-trixie

COPY --from=docker.io/cloudflare/sandbox:1.0.3 /usr/local/bin/sandbox-shim /usr/local/bin/sandbox-shim

# The interpreter has no third-party packages; grading also uses -I -S.
RUN rm -rf /usr/local/lib/python*/site-packages /usr/lib/python*/site-packages \
&& mkdir -p /workspace
WORKDIR /workspace
CMD ["sleep", "infinity"]
16 changes: 8 additions & 8 deletions GENERATED_APP_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,14 @@ Supported event shapes:
```ts
type AttemptEvent =
| {
type: 'answer';
questionId: string;
answer: string | string[];
correct?: boolean;
scoreGiven?: number;
scoreMaximum?: number;
timestamp: string;
}
type: 'answer';
questionId: string;
answer: string | string[];
correct?: boolean;
scoreGiven?: number;
scoreMaximum?: number;
timestamp: string;
}
| { type: 'progress'; checkpoint: string; value: number; timestamp: string }
| { type: 'complete'; timestamp: string };
```
Expand Down
Loading