diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000000..af8ec52c7f
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,14 @@
+# Optional values for development through the legacy root docker-compose.yml.
+# Copy this file to .env. Database authentication remains the safe default.
+# Never commit an institution credential or a populated .env file.
+DF_AUTH_METHOD=database
+
+# To exercise AAF locally, obtain a dedicated non-production registration from
+# the identity owner, change DF_AUTH_METHOD to aaf, and fill every field below.
+DF_AAF_ISSUER_URL=
+DF_AAF_AUDIENCE_URL=http://localhost:4200
+DF_AAF_CALLBACK_URL=http://localhost:4200/api/auth/jwt
+DF_AAF_IDENTITY_PROVIDER_URL=
+DF_AAF_UNIQUE_URL=
+DF_AAF_AUTH_SIGNOUT_URL=
+DF_SECRET_KEY_AAF=
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 06b5afd845..0000000000
--- a/.eslintignore
+++ /dev/null
@@ -1,4 +0,0 @@
-build/
-coverage/
-dist/
-docs/
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 9bf5e49a86..0000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "root": true,
- "overrides": [
- {
- "files": ["*.ts"],
- "parserOptions": {
- "project": ["tsconfig.*?.json"],
- "createDefaultProgram": true
- },
- "plugins": ["@typescript-eslint"],
- "extends": [
- "plugin:@angular-eslint/recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:prettier/recommended"
- ],
- "env": {
- "browser": true,
- "jasmine": true
- },
- "rules": {
- "@typescript-eslint/no-unused-vars": [
- "warn",
- {
- "args": "all",
- "argsIgnorePattern": "^_",
- "caughtErrors": "all",
- "caughtErrorsIgnorePattern": "^_",
- "destructuredArrayIgnorePattern": "^_",
- "varsIgnorePattern": "^_",
- "ignoreRestSiblings": true
- }
- ],
- "@angular-eslint/no-empty-lifecycle-method": "warn",
- "@angular-eslint/component-class-suffix": "warn",
- "@angular-eslint/no-output-on-prefix": "warn",
- "@typescript-eslint/no-inferrable-types": "off",
- "@angular-eslint/directive-selector": [
- "warn",
- {
- "type": "attribute",
- "prefix": "f",
- "style": "camelCase"
- }
- ],
- "@angular-eslint/component-selector": [
- "warn",
- {
- "type": "element",
- "prefix": "f",
- "style": "kebab-case"
- }
- ],
- "@typescript-eslint/ban-types": "warn",
- "@typescript-eslint/no-empty-function": "warn",
- "@typescript-eslint/no-inferrable-types": "off",
- "@typescript-eslint/no-this-alias": "warn",
- "no-dupe-class-members": "warn",
- "no-prototype-builtins": "warn",
- "no-unused-vars": "off",
- "no-useless-escape": "warn",
- "no-var": "warn",
- "quotes": [
- "warn",
- "single",
- {
- "allowTemplateLiterals": true
- }
- ],
- "prefer-const": "warn",
- "prettier/prettier": "warn"
- }
- },
- {
- "files": ["*.component.html"],
- "extends": ["plugin:@angular-eslint/template/recommended", "plugin:prettier/recommended"],
- "rules": {
- "max-len": [
- "warn",
- {
- "code": 140
- }
- ],
- "prettier/prettier": "warn"
- }
- },
- {
- "files": ["*.component.ts"],
- "extends": ["plugin:@angular-eslint/template/process-inline-templates"]
- }
- ]
-}
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000000..260dc5e2fd
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,5 @@
+# 8 June 2026: Repository-wide formatting and lint configuration
+26b4962794d16e90587fb179aab966b39459050c
+
+# 17 June 2026: Repository-wide sorting of HTML attributes
+1ffc7ad7b9dc18f22be32bf6313bfde97957dd2f
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..d56abbf304
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto eol=lf
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000000..1cc4d7d71b
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,5 @@
+# The App key is available only to the default-branch policy job. Keep every
+# workflow and the evaluator itself under lead review.
+/.github/workflows/ @ontrack-features-t2-2026/ontrack-leads
+/.github/review-policy/ @ontrack-features-t2-2026/ontrack-leads
+/.github/CODEOWNERS @ontrack-features-t2-2026/ontrack-leads
diff --git a/.github/review-policy/README.md b/.github/review-policy/README.md
new file mode 100644
index 0000000000..bcf9e76f76
--- /dev/null
+++ b/.github/review-policy/README.md
@@ -0,0 +1,51 @@
+# OnTrack pull-request review policy
+
+The required status context `ontrack/review-policy` passes when the current pull
+request head has either:
+
+- one approval from a current `ontrack-leads` member; or
+- two approvals from distinct current `ontrack-contributors` members.
+
+Approvals from the pull-request author, bots, stale commits, dismissed reviews,
+or reviewers whose latest actionable review requests changes do not count.
+
+## Security model
+
+`ontrack-review-policy-signal.yml` is unprivileged and never checks out pull-request
+code. A completed signal wakes `ontrack-review-policy.yml` through `workflow_run`.
+The evaluator workflow checks out only this directory from the protected default
+branch, then mints a short-lived token for the organization-owned GitHub App.
+
+The App is installed only on the three OnTrack Doubtfire repositories and has:
+
+- organization Members: read;
+- repository Metadata: read (mandatory);
+- repository Pull requests: read; and
+- repository Commit statuses: write.
+
+The App has no contents, workflow, administration, merge, or webhook permission.
+Its private key is held in `ONTRACK_REVIEW_APP_PRIVATE_KEY` in the
+`ontrack-review-policy` environment, which only permits the protected `11.0.x`
+branch. Its numeric App ID is held in `ONTRACK_REVIEW_APP_ID`.
+
+The evaluator reports on the pull-request head commit. GitHub gates on the test merge
+commit whenever that commit carries a status and only falls back to the head when it
+carries none, so reporting on the test merge commit would move the merge gate onto a
+commit that carries none of this repository's other checks. The head is also stable,
+where the test merge commit is recomputed every time the base branch moves.
+A five-minute reconciliation covers team membership and base-branch changes that
+do not emit a pull-request review event. Unchanged results are not republished,
+which avoids GitHub's per-commit status limit.
+
+## Ruleset integration
+
+Keep the native one-overall-approval rule, stale-review dismissal, and conversation
+resolution. Require `ontrack/review-policy` with the OnTrack Review Policy App as
+its expected source. Remove the native `ontrack-leads` required-reviewer entry only
+after the App status has been observed and made required; otherwise GitHub combines
+the native team rules with AND semantics.
+
+Changes to any workflow, the evaluator, or CODEOWNERS should continue to require
+one `ontrack-leads` approval through a path-specific native reviewer rule. This is
+necessary because any default-branch workflow could otherwise reference the App's
+environment secret.
diff --git a/.github/review-policy/evaluate.mjs b/.github/review-policy/evaluate.mjs
new file mode 100644
index 0000000000..f9dfa6fc45
--- /dev/null
+++ b/.github/review-policy/evaluate.mjs
@@ -0,0 +1,529 @@
+import { createSign } from 'node:crypto';
+import { readFile } from 'node:fs/promises';
+import { pathToFileURL } from 'node:url';
+
+const API_VERSION = '2022-11-28';
+const POLICY_CONTEXT = 'ontrack/review-policy';
+const APP_BOT_LOGIN = 'ontrack-review-policy-t2-2026[bot]';
+const PAGE_SIZE = 100;
+const MAX_PAGES = 50;
+const ALLOWED_REPOSITORIES = new Set([
+ 'doubtfire-deploy',
+ 'doubtfire-api',
+ 'doubtfire-web',
+]);
+
+function normalizeLogin(login) {
+ return String(login || '').toLowerCase();
+}
+
+function encodeJson(value) {
+ return Buffer.from(JSON.stringify(value)).toString('base64url');
+}
+
+export function createAppJwt(appId, privateKey, nowSeconds = Math.floor(Date.now() / 1000)) {
+ if (!/^\d+$/.test(String(appId))) {
+ throw new Error('ONTRACK_REVIEW_APP_ID must be a numeric GitHub App ID.');
+ }
+ if (!String(privateKey).includes('PRIVATE KEY')) {
+ throw new Error('ONTRACK_REVIEW_APP_PRIVATE_KEY is missing or invalid.');
+ }
+
+ const header = encodeJson({ alg: 'RS256', typ: 'JWT' });
+ const payload = encodeJson({
+ iat: nowSeconds - 60,
+ exp: nowSeconds + 540,
+ iss: String(appId),
+ });
+ const unsigned = `${header}.${payload}`;
+ const signer = createSign('RSA-SHA256');
+ signer.update(unsigned);
+ signer.end();
+ const signature = signer.sign(privateKey).toString('base64url');
+ return `${unsigned}.${signature}`;
+}
+
+export function approvedReviewers(reviews, headSha, authorLogin) {
+ const author = normalizeLogin(authorLogin);
+ const latestActionableReview = new Map();
+ const actionableStates = new Set(['APPROVED', 'CHANGES_REQUESTED', 'DISMISSED']);
+
+ const ordered = [...reviews].sort((left, right) => {
+ const leftTime = Date.parse(left.submitted_at || 0) || 0;
+ const rightTime = Date.parse(right.submitted_at || 0) || 0;
+ return leftTime - rightTime || Number(left.id || 0) - Number(right.id || 0);
+ });
+
+ for (const review of ordered) {
+ const login = normalizeLogin(review.user?.login);
+ const state = String(review.state || '').toUpperCase();
+ if (!login || login === author || review.user?.type === 'Bot') {
+ continue;
+ }
+ // A comment after an approval does not revoke the approval.
+ if (!actionableStates.has(state)) {
+ continue;
+ }
+ latestActionableReview.set(login, review);
+ }
+
+ return new Set(
+ [...latestActionableReview.entries()]
+ .filter(([, review]) => (
+ String(review.state || '').toUpperCase() === 'APPROVED'
+ && review.commit_id === headSha
+ ))
+ .map(([login]) => login),
+ );
+}
+
+export function evaluatePolicy(approved, leadMembers, contributorMembers) {
+ const leads = new Set([...leadMembers].map(normalizeLogin));
+ const contributors = new Set([...contributorMembers].map(normalizeLogin));
+ let leadApprovals = 0;
+ let contributorApprovals = 0;
+
+ for (const login of approved) {
+ const normalized = normalizeLogin(login);
+ if (leads.has(normalized)) {
+ leadApprovals += 1;
+ }
+ if (contributors.has(normalized)) {
+ contributorApprovals += 1;
+ }
+ }
+
+ return {
+ leadApprovals,
+ contributorApprovals,
+ passes: leadApprovals >= 1 || contributorApprovals >= 2,
+ };
+}
+
+export function pullRequestNumbersFromWorkflowRun(workflowRun) {
+ const numbers = new Set();
+ for (const pullRequest of workflowRun?.pull_requests || []) {
+ const number = Number(pullRequest?.number);
+ if (Number.isSafeInteger(number) && number > 0) {
+ numbers.add(number);
+ }
+ }
+
+ const title = String(workflowRun?.display_title || '');
+ const titleMatch = title.match(/\bPR #([1-9]\d{0,9})\b/);
+ if (titleMatch) {
+ const number = Number(titleMatch[1]);
+ if (Number.isSafeInteger(number)) {
+ numbers.add(number);
+ }
+ }
+ return [...numbers];
+}
+
+function safeError(error) {
+ return String(error?.message || error || 'Unknown error')
+ .replace(/gh[opsu]_[A-Za-z0-9_]+/g, '[redacted token]')
+ .replace(
+ /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/g,
+ '[redacted private key]',
+ )
+ .slice(0, 500);
+}
+
+function repositoryParts(repository) {
+ const match = String(repository || '').match(/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/);
+ if (!match) {
+ throw new Error('GITHUB_REPOSITORY is invalid.');
+ }
+ return { owner: match[1], repo: match[2] };
+}
+
+class GitHubApi {
+ constructor(apiUrl, token) {
+ this.apiUrl = String(apiUrl || 'https://api.github.com').replace(/\/$/, '');
+ this.token = token;
+ }
+
+ async request(path, { method = 'GET', body, expected = [200] } = {}) {
+ const response = await fetch(`${this.apiUrl}${path}`, {
+ method,
+ headers: {
+ Accept: 'application/vnd.github+json',
+ Authorization: `Bearer ${this.token}`,
+ 'User-Agent': 'ontrack-review-policy',
+ 'X-GitHub-Api-Version': API_VERSION,
+ },
+ body: body === undefined ? undefined : JSON.stringify(body),
+ });
+
+ if (!expected.includes(response.status)) {
+ const requestId = response.headers.get('x-github-request-id');
+ throw new Error(
+ `GitHub API ${method} ${path} returned ${response.status}`
+ + (requestId ? ` (request ${requestId})` : ''),
+ );
+ }
+
+ if (response.status === 204) {
+ return null;
+ }
+ const text = await response.text();
+ return text ? JSON.parse(text) : null;
+ }
+
+ async paginate(path) {
+ const items = [];
+ const separator = path.includes('?') ? '&' : '?';
+ for (let page = 1; page <= MAX_PAGES; page += 1) {
+ const batch = await this.request(
+ `${path}${separator}per_page=${PAGE_SIZE}&page=${page}`,
+ );
+ if (!Array.isArray(batch)) {
+ throw new Error(`Expected a list from GitHub API path ${path}.`);
+ }
+ items.push(...batch);
+ if (batch.length < PAGE_SIZE) {
+ return items;
+ }
+ }
+ throw new Error(`GitHub API pagination exceeded ${MAX_PAGES} pages for ${path}.`);
+ }
+}
+
+async function mintInstallationToken({ apiUrl, owner, repo, appId, privateKey }) {
+ const appJwt = createAppJwt(appId, privateKey);
+ const appApi = new GitHubApi(apiUrl, appJwt);
+ const installation = await appApi.request(
+ `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/installation`,
+ );
+ const tokenResponse = await appApi.request(
+ `/app/installations/${installation.id}/access_tokens`,
+ {
+ method: 'POST',
+ expected: [201],
+ body: {
+ repositories: [repo],
+ permissions: {
+ members: 'read',
+ pull_requests: 'read',
+ statuses: 'write',
+ },
+ },
+ },
+ );
+
+ if (!tokenResponse?.token) {
+ throw new Error('GitHub did not return an installation access token.');
+ }
+ // Generated tokens are not repository secrets, so mask them explicitly.
+ console.log(`::add-mask::${tokenResponse.token}`);
+ return tokenResponse.token;
+}
+
+async function teamMembers(api, owner, teamSlug) {
+ const members = await api.paginate(
+ `/orgs/${encodeURIComponent(owner)}/teams/${encodeURIComponent(teamSlug)}/members`,
+ );
+ return new Set(members.map((member) => normalizeLogin(member.login)));
+}
+
+async function openPullRequests(api, owner, repo) {
+ return api.paginate(
+ `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls?state=open`,
+ );
+}
+
+async function pullRequest(api, owner, repo, number) {
+ return api.request(
+ `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${number}`,
+ );
+}
+
+async function reviewsForPullRequest(api, owner, repo, number) {
+ return api.paginate(
+ `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${number}/reviews`,
+ );
+}
+
+function runUrl(repository, runId) {
+ return `https://github.com/${repository}/actions/runs/${runId}`;
+}
+
+async function setPolicyStatus(api, owner, repo, sha, state, description, targetUrl) {
+ if (!/^[0-9a-f]{40}$/i.test(String(sha || ''))) {
+ throw new Error('Cannot publish the review policy without a valid commit SHA.');
+ }
+ const clippedDescription = description.slice(0, 140);
+ try {
+ const statuses = await api.paginate(
+ `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`
+ + `/commits/${encodeURIComponent(sha)}/statuses`,
+ );
+ const latest = statuses.find((status) => (
+ status.context === POLICY_CONTEXT
+ && normalizeLogin(status.creator?.login) === APP_BOT_LOGIN
+ ));
+ if (latest?.state === state && latest?.description === clippedDescription) {
+ return false;
+ }
+ } catch (error) {
+ // Deduplication is only an optimization. Always attempt the fail-closed write
+ // when status history cannot be read but the status endpoint may still work.
+ console.warn(`::warning::Status deduplication failed: ${safeError(error)}`);
+ }
+
+ await api.request(
+ `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/statuses/${sha}`,
+ {
+ method: 'POST',
+ expected: [201],
+ body: {
+ state,
+ context: POLICY_CONTEXT,
+ description: clippedDescription,
+ target_url: targetUrl,
+ },
+ },
+ );
+ return true;
+}
+
+export { setPolicyStatus };
+
+// Report on the pull-request head. GitHub gates on the test merge commit whenever that
+// commit carries a status and only falls back to the head when it carries none, and the
+// test merge commit carries none of this repository's checks. Reporting there would move
+// the whole merge gate onto a commit that CI never sees. The head is also stable while
+// the test merge commit is recomputed every time the base branch moves.
+export function statusShaForPullRequest(pullRequestToReport) {
+ return pullRequestToReport.head?.sha;
+}
+
+function reviewDigest(reviews) {
+ return reviews
+ .map((review) => (
+ `${review.id}:${review.state}:${review.commit_id}:`
+ + `${review.submitted_at}:${review.user?.id}:${review.user?.login}`
+ ))
+ .sort()
+ .join('|');
+}
+
+function samePullRequestVersion(left, right) {
+ return (
+ left.state === right.state
+ && left.draft === right.draft
+ && left.head?.sha === right.head?.sha
+ && left.base?.ref === right.base?.ref
+ && left.base?.sha === right.base?.sha
+ && left.mergeable === right.mergeable
+ );
+}
+
+async function evaluatePullRequest({
+ api,
+ owner,
+ repo,
+ pullRequest: current,
+ leads,
+ contributors,
+ targetUrl,
+ attempt = 0,
+}) {
+ // Re-fetch before evaluating so a delayed workflow_run never trusts its event's
+ // old head or merge SHA.
+ current = await pullRequest(api, owner, repo, current.number);
+ if (current.state !== 'open') {
+ return;
+ }
+
+ const statusSha = statusShaForPullRequest(current);
+ if (current.draft) {
+ await setPolicyStatus(
+ api,
+ owner,
+ repo,
+ statusSha,
+ 'pending',
+ 'Waiting for the pull request to be marked ready for review',
+ targetUrl,
+ );
+ console.log(`PR #${current.number}: draft`);
+ return;
+ }
+
+ const firstReviews = await reviewsForPullRequest(api, owner, repo, current.number);
+ const checked = await pullRequest(api, owner, repo, current.number);
+ const secondReviews = await reviewsForPullRequest(api, owner, repo, current.number);
+ const live = await pullRequest(api, owner, repo, current.number);
+ if (checked.state !== 'open' || live.state !== 'open') {
+ return;
+ }
+ const liveStatusSha = statusShaForPullRequest(live);
+ if (
+ !samePullRequestVersion(current, checked)
+ || !samePullRequestVersion(checked, live)
+ || reviewDigest(firstReviews) !== reviewDigest(secondReviews)
+ || liveStatusSha !== statusSha
+ ) {
+ if (attempt >= 1) {
+ throw new Error('Pull request changed repeatedly during evaluation.');
+ }
+ console.log(`PR #${current.number}: changed during evaluation; retrying once`);
+ return evaluatePullRequest({
+ api,
+ owner,
+ repo,
+ pullRequest: live,
+ leads,
+ contributors,
+ targetUrl,
+ attempt: attempt + 1,
+ });
+ }
+
+ const approved = approvedReviewers(
+ secondReviews,
+ live.head.sha,
+ live.user?.login,
+ );
+ const result = evaluatePolicy(approved, leads, contributors);
+ const state = result.passes ? 'success' : 'pending';
+ const description = result.passes
+ ? `Passed: ${result.leadApprovals}/1 lead or ${result.contributorApprovals}/2 contributors`
+ : `Waiting: ${result.leadApprovals}/1 lead or ${result.contributorApprovals}/2 contributors`;
+
+ await setPolicyStatus(api, owner, repo, liveStatusSha, state, description, targetUrl);
+ console.log(
+ `PR #${current.number}: lead=${result.leadApprovals}, `
+ + `contributors=${result.contributorApprovals}, status=${state}`,
+ );
+}
+
+async function eventPayload() {
+ const payloadPath = process.env.GITHUB_EVENT_PATH;
+ if (!payloadPath) {
+ return {};
+ }
+ return JSON.parse(await readFile(payloadPath, 'utf8'));
+}
+
+async function pullRequestsToEvaluate(api, owner, repo, eventName, payload) {
+ const open = await openPullRequests(api, owner, repo);
+ if (eventName === 'workflow_run') {
+ // Treat workflow_run fields only as untrusted locators. Match them against
+ // live open PRs fetched with the App token before evaluating anything.
+ const numbers = new Set(pullRequestNumbersFromWorkflowRun(payload.workflow_run));
+ const headSha = payload.workflow_run?.head_sha;
+ const linked = open.filter((candidate) => (
+ numbers.has(candidate.number)
+ || candidate.head?.sha === headSha
+ || candidate.merge_commit_sha === headSha
+ ));
+ return linked.length > 0 ? linked : open;
+ }
+ return open;
+}
+
+export async function main() {
+ const repository = process.env.GITHUB_REPOSITORY;
+ const { owner, repo } = repositoryParts(repository);
+ if (owner !== 'ontrack-features-t2-2026' || !ALLOWED_REPOSITORIES.has(repo)) {
+ throw new Error('This evaluator only runs for the three approved OnTrack repositories.');
+ }
+
+ const appId = process.env.ONTRACK_REVIEW_APP_ID;
+ const privateKey = process.env.ONTRACK_REVIEW_APP_PRIVATE_KEY;
+ const apiUrl = process.env.GITHUB_API_URL || 'https://api.github.com';
+ const token = await mintInstallationToken({
+ apiUrl,
+ owner,
+ repo,
+ appId,
+ privateKey,
+ });
+ const api = new GitHubApi(apiUrl, token);
+ const payload = await eventPayload();
+ const eventName = process.env.GITHUB_EVENT_NAME || '';
+ const pullRequests = await pullRequestsToEvaluate(api, owner, repo, eventName, payload);
+
+ if (pullRequests.length === 0) {
+ console.log('No open pull requests require review-policy evaluation.');
+ return;
+ }
+
+ const leadTeam = process.env.ONTRACK_LEAD_TEAM || 'ontrack-leads';
+ const contributorTeam = process.env.ONTRACK_CONTRIBUTOR_TEAM || 'ontrack-contributors';
+ let leads;
+ let contributors;
+ try {
+ [leads, contributors] = await Promise.all([
+ teamMembers(api, owner, leadTeam),
+ teamMembers(api, owner, contributorTeam),
+ ]);
+ } catch (error) {
+ const targetUrl = runUrl(repository, process.env.GITHUB_RUN_ID);
+ for (const current of pullRequests) {
+ try {
+ const sha = statusShaForPullRequest(current);
+ await setPolicyStatus(
+ api,
+ owner,
+ repo,
+ sha,
+ 'error',
+ 'OnTrack team membership could not be verified',
+ targetUrl,
+ );
+ } catch (statusError) {
+ console.error(`::error::${safeError(statusError)}`);
+ }
+ }
+ throw error;
+ }
+
+ const targetUrl = runUrl(repository, process.env.GITHUB_RUN_ID);
+ const failures = [];
+ for (const current of pullRequests) {
+ try {
+ await evaluatePullRequest({
+ api,
+ owner,
+ repo,
+ pullRequest: current,
+ leads,
+ contributors,
+ targetUrl,
+ });
+ } catch (error) {
+ failures.push(error);
+ try {
+ const sha = statusShaForPullRequest(current);
+ await setPolicyStatus(
+ api,
+ owner,
+ repo,
+ sha,
+ 'error',
+ 'OnTrack review policy evaluation failed',
+ targetUrl,
+ );
+ } catch (statusError) {
+ console.error(`::error::${safeError(statusError)}`);
+ }
+ console.error(`::error::PR #${current.number}: ${safeError(error)}`);
+ }
+ }
+
+ if (failures.length > 0) {
+ throw new Error(`${failures.length} pull-request evaluation(s) failed.`);
+ }
+}
+
+const invokedPath = process.argv[1] ? pathToFileURL(process.argv[1]).href : '';
+if (import.meta.url === invokedPath) {
+ main().catch((error) => {
+ console.error(`::error::${safeError(error)}`);
+ process.exitCode = 1;
+ });
+}
diff --git a/.github/review-policy/evaluate.test.mjs b/.github/review-policy/evaluate.test.mjs
new file mode 100644
index 0000000000..0c093524a5
--- /dev/null
+++ b/.github/review-policy/evaluate.test.mjs
@@ -0,0 +1,236 @@
+import assert from 'node:assert/strict';
+import { generateKeyPairSync, verify } from 'node:crypto';
+import { test } from 'node:test';
+
+import {
+ approvedReviewers,
+ createAppJwt,
+ evaluatePolicy,
+ pullRequestNumbersFromWorkflowRun,
+ setPolicyStatus,
+ statusShaForPullRequest,
+} from './evaluate.mjs';
+
+function review({
+ id,
+ login,
+ state = 'APPROVED',
+ commit = 'head',
+ submitted = `2026-08-24T00:00:${String(id).padStart(2, '0')}Z`,
+ type = 'User',
+}) {
+ return {
+ id,
+ state,
+ commit_id: commit,
+ submitted_at: submitted,
+ user: { login, type },
+ };
+}
+
+test('one lead approval passes', () => {
+ const result = evaluatePolicy(
+ new Set(['lead']),
+ new Set(['lead']),
+ new Set(['lead', 'contributor']),
+ );
+ assert.equal(result.passes, true);
+ assert.equal(result.leadApprovals, 1);
+});
+
+test('two distinct contributor approvals pass', () => {
+ const result = evaluatePolicy(
+ new Set(['contributor-a', 'contributor-b']),
+ new Set(['lead']),
+ new Set(['lead', 'contributor-a', 'contributor-b']),
+ );
+ assert.equal(result.passes, true);
+ assert.equal(result.contributorApprovals, 2);
+});
+
+test('one contributor approval does not pass', () => {
+ const result = evaluatePolicy(
+ new Set(['contributor-a']),
+ new Set(['lead']),
+ new Set(['lead', 'contributor-a']),
+ );
+ assert.equal(result.passes, false);
+});
+
+test('duplicate approvals from one reviewer count once', () => {
+ const approved = approvedReviewers([
+ review({ id: 1, login: 'Contributor-A' }),
+ review({ id: 2, login: 'contributor-a' }),
+ ], 'head', 'author');
+ assert.deepEqual([...approved], ['contributor-a']);
+});
+
+test('a later comment does not revoke an approval', () => {
+ const approved = approvedReviewers([
+ review({ id: 1, login: 'contributor-a' }),
+ review({ id: 2, login: 'contributor-a', state: 'COMMENTED' }),
+ ], 'head', 'author');
+ assert.deepEqual([...approved], ['contributor-a']);
+});
+
+test('a later changes-requested review revokes an approval', () => {
+ const approved = approvedReviewers([
+ review({ id: 1, login: 'contributor-a' }),
+ review({ id: 2, login: 'contributor-a', state: 'CHANGES_REQUESTED' }),
+ ], 'head', 'author');
+ assert.deepEqual([...approved], []);
+});
+
+test('stale, author, and bot approvals are ignored', () => {
+ const approved = approvedReviewers([
+ review({ id: 1, login: 'stale', commit: 'old-head' }),
+ review({ id: 2, login: 'author' }),
+ review({ id: 3, login: 'review-bot[bot]', type: 'Bot' }),
+ ], 'head', 'author');
+ assert.deepEqual([...approved], []);
+});
+
+test('workflow run PR numbers are deduplicated and validated', () => {
+ assert.deepEqual(
+ pullRequestNumbersFromWorkflowRun({
+ display_title: 'OnTrack review policy signal for PR #42',
+ pull_requests: [{ number: 42 }, { number: 17 }, { number: 0 }],
+ }),
+ [42, 17],
+ );
+});
+
+test('workflow run ignores unsafe or implausibly large PR numbers', () => {
+ assert.deepEqual(
+ pullRequestNumbersFromWorkflowRun({
+ display_title: 'OnTrack review policy signal for PR #12345678901',
+ pull_requests: [{ number: Number.MAX_SAFE_INTEGER + 1 }, { number: -4 }],
+ }),
+ [],
+ );
+});
+
+test('GitHub App JWT has a valid RSA signature and bounded lifetime', () => {
+ const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
+ const privateKeyPem = privateKey.export({ type: 'pkcs8', format: 'pem' });
+ const now = 1_800_000_000;
+ const jwt = createAppJwt('4699573', privateKeyPem, now);
+ const [header, payload, signature] = jwt.split('.');
+ assert.equal(
+ verify(
+ 'RSA-SHA256',
+ Buffer.from(`${header}.${payload}`),
+ publicKey,
+ Buffer.from(signature, 'base64url'),
+ ),
+ true,
+ );
+ const claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
+ assert.equal(claims.iss, '4699573');
+ assert.equal(claims.iat, now - 60);
+ assert.equal(claims.exp, now + 540);
+});
+
+test('unchanged App status is not republished and spoofed sources are ignored', async () => {
+ const posts = [];
+ const api = {
+ async paginate() {
+ return [
+ {
+ context: 'ontrack/review-policy',
+ state: 'success',
+ description: 'Passed',
+ creator: { login: 'not-the-policy-app[bot]' },
+ },
+ {
+ context: 'ontrack/review-policy',
+ state: 'pending',
+ description: 'Waiting',
+ creator: { login: 'ontrack-review-policy-t2-2026[bot]' },
+ },
+ ];
+ },
+ async request(path, options) {
+ posts.push({ path, options });
+ return {};
+ },
+ };
+
+ assert.equal(
+ await setPolicyStatus(
+ api,
+ 'owner',
+ 'repo',
+ 'a'.repeat(40),
+ 'pending',
+ 'Waiting',
+ 'https://example.test/run',
+ ),
+ false,
+ );
+ assert.equal(posts.length, 0);
+
+ assert.equal(
+ await setPolicyStatus(
+ api,
+ 'owner',
+ 'repo',
+ 'a'.repeat(40),
+ 'success',
+ 'Passed',
+ 'https://example.test/run',
+ ),
+ true,
+ );
+ assert.equal(posts.length, 1);
+});
+
+test('status-history failure does not suppress a fail-closed write', async () => {
+ const posts = [];
+ const warnings = [];
+ const originalWarn = console.warn;
+ console.warn = (message) => warnings.push(message);
+ const api = {
+ async paginate() {
+ throw new Error('history unavailable');
+ },
+ async request(path, options) {
+ posts.push({ path, options });
+ return {};
+ },
+ };
+
+ try {
+ assert.equal(
+ await setPolicyStatus(
+ api,
+ 'owner',
+ 'repo',
+ 'b'.repeat(40),
+ 'error',
+ 'Evaluation failed',
+ 'https://example.test/run',
+ ),
+ true,
+ );
+ assert.equal(posts.length, 1);
+ assert.equal(posts[0].options.body.state, 'error');
+ assert.equal(warnings.length, 1);
+ } finally {
+ console.warn = originalWarn;
+ }
+});
+
+test('the status is reported on the head, never on the test merge commit', () => {
+ assert.equal(
+ statusShaForPullRequest({ head: { sha: 'head' }, merge_commit_sha: 'test-merge' }),
+ 'head',
+ );
+});
+
+test('a conflicting pull request with no test merge commit still reports on its head', () => {
+ assert.equal(
+ statusShaForPullRequest({ head: { sha: 'head' }, merge_commit_sha: null }),
+ 'head',
+ );
+});
diff --git a/.github/workflows/deployment-institution.yml b/.github/workflows/deployment-institution.yml
new file mode 100644
index 0000000000..d326026b90
--- /dev/null
+++ b/.github/workflows/deployment-institution.yml
@@ -0,0 +1,76 @@
+name: create-institution-deployment
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+permissions:
+ contents: read
+
+jobs:
+ docker-web-server:
+ if: github.repository_owner == 'doubtfire-lms'
+ environment: deployment-secrets
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
+ with:
+ submodules: recursive
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
+ - name: Check custom institution deployment secrets
+ id: custom_config
+ env:
+ INSTITUTION_DOCKERHUB_USERNAME: ${{ secrets.INSTITUTION_DOCKERHUB_USERNAME }}
+ INSTITUTION_DOCKERHUB_TOKEN: ${{ secrets.INSTITUTION_DOCKERHUB_TOKEN }}
+ INSTITUTION: ${{ secrets.INSTITUTION }}
+ run: |
+ if [ -n "$INSTITUTION_DOCKERHUB_USERNAME" ] && [ -n "$INSTITUTION_DOCKERHUB_TOKEN" ] && [ -n "$INSTITUTION" ]; then
+ echo "enabled=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "enabled=false" >> "$GITHUB_OUTPUT"
+ echo "Skipping custom institution Docker publish because one or more institution secrets are not set."
+ fi
+ - name: Login to DockerHub
+ uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
+ if: github.event_name != 'pull_request' && steps.custom_config.outputs.enabled == 'true'
+ with:
+ username: ${{ secrets.INSTITUTION_DOCKERHUB_USERNAME }}
+ password: ${{ secrets.INSTITUTION_DOCKERHUB_TOKEN }}
+ - name: Setup meta for custom institution web server
+ id: docker_meta
+ uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
+ if: steps.custom_config.outputs.enabled == 'true'
+ with:
+ images: ${{ secrets.INSTITUTION_DOCKERHUB_USERNAME }}/${{ secrets.INSTITUTION }}
+ tags: |
+ type=ref,event=tag
+ type=ref,event=branch
+ type=semver,pattern=prod-{{version}}
+ type=semver,pattern=prod-{{major}}.{{minor}}
+ type=semver,pattern=prod-{{major}}
+ - name: Build and push custom web server
+ id: docker_build
+ uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
+ if: steps.custom_config.outputs.enabled == 'true'
+ with:
+ file: deploy.Dockerfile
+ context: .
+ push: ${{ github.event_name != 'pull_request' }}
+ tags: ${{ steps.docker_meta.outputs.tags }}
+ labels: ${{ steps.docker_meta.outputs.labels }}
+ sbom: true
+ provenance: mode=max
+ build-args: |
+ SENTRY_DSN=${{ secrets.SENTRY_DSN }}
+ SENTRY_ORG=${{ secrets.SENTRY_ORG }}
+ SENTRY_PROJECT=${{ secrets.SENTRY_PROJECT }}
+ SENTRY_RELEASE=${{ github.ref_name }}
+ SENTRY_DIST=${{ github.run_number }}
+ UPLOAD_SENTRY_SOURCEMAPS=true
+ secrets: |
+ sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}
+ - name: Image digest
+ if: steps.custom_config.outputs.enabled == 'true'
+ run: echo ${{ steps.docker_build.outputs.digest }}
diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
index 124fe4c2ae..507dfb6c2a 100644
--- a/.github/workflows/deployment.yml
+++ b/.github/workflows/deployment.yml
@@ -10,56 +10,60 @@ on:
# deployment:
# workflow_dispatch:
jobs:
- docker-deploy-development-image:
- if: github.repository_owner == 'doubtfire-lms'
- environment: deployment-secrets
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
- - name: Login to DockerHub
- uses: docker/login-action@v3
- if: github.event_name != 'pull_request'
- with:
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- - name: Setup meta for development image
- id: docker_meta
- uses: docker/metadata-action@v5
- with:
- images: lmsdoubtfire/doubtfire-web
- tags: |
- type=semver,pattern={{major}}.{{minor}}.x-dev
- - name: Build and push web server
- id: docker_build
- uses: docker/build-push-action@v5
- with:
- context: .
- push: ${{ github.event_name != 'pull_request' }}
- tags: ${{ steps.docker_meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
- - name: Image digest
- run: echo ${{ steps.docker_build.outputs.digest }}
+ # docker-deploy-development-image:
+ # if: github.repository_owner == 'doubtfire-lms'
+ # environment: deployment-secrets
+ # runs-on: ubuntu-latest
+ # steps:
+ # - name: Checkout code
+ # uses: actions/checkout@v4
+ # with:
+ # submodules: recursive
+ # - name: Set up Docker Buildx
+ # uses: docker/setup-buildx-action@v3
+ # - name: Login to DockerHub
+ # uses: docker/login-action@v3
+ # if: github.event_name != 'pull_request'
+ # with:
+ # username: ${{ secrets.DOCKERHUB_USERNAME }}
+ # password: ${{ secrets.DOCKERHUB_TOKEN }}
+ # - name: Setup meta for development image
+ # id: docker_meta
+ # uses: docker/metadata-action@v5
+ # with:
+ # images: lmsdoubtfire/doubtfire-web
+ # tags: |
+ # type=semver,pattern={{major}}.{{minor}}.x-dev
+ # - name: Build and push web server
+ # id: docker_build
+ # uses: docker/build-push-action@v5
+ # with:
+ # context: .
+ # push: ${{ github.event_name != 'pull_request' }}
+ # tags: ${{ steps.docker_meta.outputs.tags }}
+ # labels: ${{ steps.docker_meta.outputs.labels }}
+ # - name: Image digest
+ # run: echo ${{ steps.docker_build.outputs.digest }}
docker-web-server:
if: github.repository_owner == 'doubtfire-lms'
environment: deployment-secrets
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
+ with:
+ submodules: recursive
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
+ uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
- name: Login to DockerHub
- uses: docker/login-action@v3
+ uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup meta for web server
id: docker_meta
- uses: docker/metadata-action@v5
+ uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
with:
images: lmsdoubtfire/doubtfire-web
tags: |
@@ -70,12 +74,14 @@ jobs:
type=semver,pattern=prod-{{major}}
- name: Build and push web server
id: docker_build
- uses: docker/build-push-action@v5
+ uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
with:
file: deploy.Dockerfile
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
+ labels: ${{ steps.docker_meta.outputs.labels }}
+ sbom: true
+ provenance: mode=max
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000000..053acc04a6
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,23 @@
+name: Lint CI
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [22]
+
+ steps:
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: npm ci
+ - run: npm run lint
+ - run: npm run typecheck
diff --git a/.github/workflows/nodejs-ci.yml b/.github/workflows/nodejs-ci.yml
index 3ef6877d51..dd2f6e9990 100644
--- a/.github/workflows/nodejs-ci.yml
+++ b/.github/workflows/nodejs-ci.yml
@@ -13,16 +13,16 @@ jobs:
strategy:
matrix:
- node-version: [20]
+ node-version: [22]
steps:
- - uses: actions/checkout@v4
- - uses: browser-actions/setup-chrome@latest
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
+ with:
+ submodules: recursive
+ - uses: browser-actions/setup-chrome@48ad923757ca74d66703209fe939badbdf80f2f4 # v2
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v3
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: ${{ matrix.node-version }}
- - run: npm install -g @angular/cli
- run: npm ci
- # - run: npm run lint
- # - run: npm run test:ci
+ - run: npm run verify:deployment-config
- run: npm run build --if-present
diff --git a/.github/workflows/notify-teams-pr.yml b/.github/workflows/notify-teams-pr.yml
new file mode 100644
index 0000000000..164a85b240
--- /dev/null
+++ b/.github/workflows/notify-teams-pr.yml
@@ -0,0 +1,260 @@
+# NOTE ON THE BASE BRANCH. GitHub loads a `pull_request_target` workflow from the
+# repository DEFAULT branch (11.0.x), not from the pull request's base branch. That
+# changed on 2025-12-08. So this file is merged into 11.0.x deliberately, against the
+# usual CONTRIBUTING rule, and a copy on feature/notifications would be dead code.
+# It fires for pull requests into every base branch, which is what we want.
+name: Notify Teams when a pull request opens
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - reopened
+ - ready_for_review
+ # - review_requested # matches "reviewer alerts" most directly, but adds one
+ # # message per requested reviewer. Decide, do not default.
+
+# Removes all GITHUB_TOKEN scopes, which this workflow does not need. It does NOT
+# restrict secrets.* - the webhook is protected by the guards below, not by this.
+permissions: {}
+
+jobs:
+ notify-teams:
+ name: Post pull request notification to Teams
+ # The job guard makes the file inert if it ever travels to thoth-tech or
+ # doubtfire-lms. The step below decides who gets a notification: a pull request
+ # from a branch in this repository always does, and a fork pull request only if
+ # its author is in one of the two OnTrack teams.
+ if: github.repository_owner == 'ontrack-features-t2-2026'
+ runs-on: ubuntu-latest
+ timeout-minutes: 2
+
+ steps:
+ - name: Check pull request author team membership
+ id: team-membership
+ shell: bash
+ env:
+ TEAM_MEMBERSHIP_TOKEN: ${{ secrets.TEAM_MEMBERSHIP_ACCESS }}
+ PR_AUTHOR: ${{ github.event.pull_request.user.login }}
+ PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
+ REPOSITORY: ${{ github.repository }}
+ run: |
+ set -euo pipefail
+
+ # A branch in this repository can only be pushed by someone who already
+ # has write access, so those pull requests need no lookup. That is also
+ # the only reason Dependabot notifies: dependabot[bot] is in neither
+ # team and 404s on both. The team check below is for fork pull requests,
+ # which is where the webhook actually needed protecting.
+ if [[ "${PR_HEAD_REPO}" == "${REPOSITORY}" ]]; then
+ echo "eligible=true" >> "${GITHUB_OUTPUT}"
+ exit 0
+ fi
+
+ if [[ -z "${TEAM_MEMBERSHIP_TOKEN:-}" ]]; then
+ echo "::error::The TEAM_MEMBERSHIP_ACCESS secret is not configured."
+ exit 1
+ fi
+
+ # The login goes into a URL path segment, so percent-encode it. curl also
+ # reads [ ] { } * in a URL as a glob and exits before sending anything, so
+ # --globoff below. Between them a login like dependabot[bot] is safe.
+ author_encoded="$(python3 -c 'import os, urllib.parse; print(urllib.parse.quote(os.environ["PR_AUTHOR"], safe=""))')"
+
+ for team in ontrack-contributors ontrack-leads; do
+ http_status="$(
+ curl \
+ --proto '=https' \
+ --tlsv1.2 \
+ --globoff \
+ --silent \
+ --show-error \
+ --connect-timeout 10 \
+ --max-time 30 \
+ --header 'Accept: application/vnd.github+json' \
+ --header "Authorization: Bearer ${TEAM_MEMBERSHIP_TOKEN}" \
+ --header 'X-GitHub-Api-Version: 2022-11-28' \
+ --output "${RUNNER_TEMP}/team-membership.json" \
+ --write-out '%{http_code}' \
+ "https://api.github.com/orgs/ontrack-features-t2-2026/teams/${team}/memberships/${author_encoded}" \
+ )" || http_status="000"
+
+ case "${http_status}" in
+ 200)
+ membership_state="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["state"])' \
+ < "${RUNNER_TEMP}/team-membership.json")"
+ # "pending" is a teammate who has not accepted the org invitation
+ # yet. They are on the team and they review, so notify them too.
+ if [[ "${membership_state}" == "active" || "${membership_state}" == "pending" ]]; then
+ echo "eligible=true" >> "${GITHUB_OUTPUT}"
+ exit 0
+ fi
+ ;;
+ 404) ;;
+ *)
+ echo "::error::GitHub returned HTTP ${http_status} while checking team membership."
+ exit 1
+ ;;
+ esac
+ done
+
+ # A skip is a decision, so say so on the run summary. Without this the job
+ # goes green and looks the same as one that posted a message.
+ echo "::warning::No Teams notification sent. ${PR_AUTHOR} opened this pull request from a fork and is in neither ontrack-contributors nor ontrack-leads."
+ echo "eligible=false" >> "${GITHUB_OUTPUT}"
+
+ - name: Build and send Teams notification
+ if: steps.team-membership.outputs.eligible == 'true'
+ shell: bash
+ env:
+ TEAMS_WEBHOOK_URL: ${{ secrets.TEAMS_PR_WEBHOOK_URL }}
+ PAYLOAD_PATH: ${{ runner.temp }}/teams-pr-notification.json
+
+ REPOSITORY: ${{ github.repository }}
+ PR_ACTION: ${{ github.event.action }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ PR_TITLE: ${{ github.event.pull_request.title }}
+ PR_AUTHOR: ${{ github.event.pull_request.user.login }}
+ PR_URL: ${{ github.event.pull_request.html_url }}
+ PR_DRAFT: ${{ github.event.pull_request.draft }}
+ PR_HEAD: ${{ github.event.pull_request.head.label }}
+ PR_BASE: ${{ github.event.pull_request.base.ref }}
+
+ run: |
+ set -euo pipefail
+
+ if [[ -z "${TEAMS_WEBHOOK_URL:-}" ]]; then
+ echo "::error::The TEAMS_PR_WEBHOOK_URL Actions secret is not configured."
+ exit 1
+ fi
+
+ python3 - <<'PY'
+ import json
+ import os
+ import re
+
+
+ def plain(value):
+ """Teams renders these fields as Markdown, and titles and branch names
+ come from anyone who can open a pull request. Removing [ ] < > and
+ backticks stops a title forging a Markdown link or code span.
+ Parentheses are left alone so `feat(scope): ...` still reads
+ properly. Note this does NOT stop Teams auto-linking a bare URL in
+ a title, it only stops the link TEXT being controlled."""
+ value = " ".join(value.split())
+ return re.sub(r"[\[\]<>`]", " ", value)[:200]
+
+
+ headline = {
+ "opened": "New pull request opened",
+ "reopened": "Pull request reopened",
+ "ready_for_review": "Pull request is ready for review",
+ }.get(os.environ["PR_ACTION"], "Pull request updated")
+
+ status = (
+ "Draft"
+ if os.environ.get("PR_DRAFT", "").lower() == "true"
+ else "Ready for review"
+ )
+
+ card = {
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
+ "type": "AdaptiveCard",
+ "version": "1.2",
+ "body": [
+ {
+ "type": "TextBlock",
+ "size": "Medium",
+ "weight": "Bolder",
+ "wrap": True,
+ "text": headline,
+ },
+ {
+ "type": "TextBlock",
+ "wrap": True,
+ "text": f"PR #{os.environ['PR_NUMBER']}: {plain(os.environ['PR_TITLE'])}",
+ },
+ {
+ "type": "FactSet",
+ "facts": [
+ {
+ "title": "Repository",
+ "value": plain(os.environ["REPOSITORY"]),
+ },
+ {
+ "title": "Author",
+ "value": plain(os.environ["PR_AUTHOR"]),
+ },
+ {
+ "title": "Status",
+ "value": status,
+ },
+ {
+ "title": "Branches",
+ "value": (
+ f"{plain(os.environ['PR_HEAD'])} -> "
+ f"{plain(os.environ['PR_BASE'])}"
+ ),
+ },
+ ],
+ },
+ ],
+ "actions": [
+ {
+ "type": "Action.OpenUrl",
+ "title": "Open pull request",
+ "url": os.environ["PR_URL"],
+ }
+ ],
+ }
+
+ payload = {
+ "type": "message",
+ "attachments": [
+ {
+ "contentType": "application/vnd.microsoft.card.adaptive",
+ "contentUrl": None,
+ "content": card,
+ }
+ ],
+ }
+
+ with open(
+ os.environ["PAYLOAD_PATH"],
+ "w",
+ encoding="utf-8",
+ ) as payload_file:
+ json.dump(
+ payload,
+ payload_file,
+ ensure_ascii=False,
+ )
+ PY
+
+ http_status="$(
+ curl \
+ --proto '=https' \
+ --tlsv1.2 \
+ --silent \
+ --show-error \
+ --connect-timeout 10 \
+ --max-time 30 \
+ --header 'Content-Type: application/json' \
+ --data-binary "@${PAYLOAD_PATH}" \
+ --output "${RUNNER_TEMP}/teams-response.txt" \
+ --write-out '%{http_code}' \
+ --url "${TEAMS_WEBHOOK_URL}"
+ )" || http_status="000"
+
+ # Uncomment to debug a failing webhook. The body comes from a third party
+ # and lands in a PUBLIC Actions log, and GitHub only masks an exact
+ # full-value match of a secret. Read it once, then comment it out again.
+ # cat "${RUNNER_TEMP}/teams-response.txt"
+
+ if [[ "${http_status}" != 2* ]]; then
+ echo "::error::Teams webhook returned HTTP ${http_status}."
+ exit 1
+ fi
+
+ echo "Teams webhook returned HTTP ${http_status} for PR #${PR_NUMBER}."
+ echo "That means the request was accepted. It does not prove the message rendered in the channel."
diff --git a/.github/workflows/ontrack-review-policy-signal.yml b/.github/workflows/ontrack-review-policy-signal.yml
new file mode 100644
index 0000000000..d87949cbbc
--- /dev/null
+++ b/.github/workflows/ontrack-review-policy-signal.yml
@@ -0,0 +1,32 @@
+name: OnTrack review policy signal
+run-name: "OnTrack review policy signal for PR #${{ github.event.pull_request.number || 'all' }}"
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - reopened
+ - synchronize
+ - edited
+ - ready_for_review
+ - converted_to_draft
+ pull_request_review:
+ types:
+ - submitted
+ - edited
+ - dismissed
+ workflow_dispatch:
+
+# This workflow deliberately has no permissions and never checks out pull-request
+# code or secrets. Its completion wakes the trusted evaluator on the default
+# branch; manual runs safely reconcile every open pull request.
+permissions: {}
+
+jobs:
+ signal:
+ if: github.repository_owner == 'ontrack-features-t2-2026'
+ runs-on: ubuntu-24.04
+ timeout-minutes: 1
+ steps:
+ - name: Signal policy evaluation
+ run: ':'
diff --git a/.github/workflows/ontrack-review-policy.yml b/.github/workflows/ontrack-review-policy.yml
new file mode 100644
index 0000000000..61c194b103
--- /dev/null
+++ b/.github/workflows/ontrack-review-policy.yml
@@ -0,0 +1,56 @@
+name: OnTrack review policy evaluator
+
+on:
+ workflow_run:
+ workflows:
+ - OnTrack review policy signal
+ types:
+ - completed
+ push:
+ branches:
+ - 11.0.x
+ schedule:
+ # Reconcile team membership and base-branch changes even when no PR event fires.
+ - cron: '3-58/5 * * * *'
+
+# The repository token is used only to check out the trusted evaluator from the
+# default branch. The GitHub App token is separately scoped to member/PR reads and
+# commit-status writes.
+permissions:
+ contents: read
+
+concurrency:
+ # Serialize signals for the same PR without allowing unrelated PR activity to
+ # replace a queued revocation. Scheduled and base-push reconciliations share a
+ # separate group. Every run re-reads authoritative GitHub state.
+ group: ontrack-review-policy-${{ github.repository }}-${{ github.event.workflow_run.pull_requests[0].number || github.event.workflow_run.display_title || 'reconcile' }}
+ cancel-in-progress: false
+
+jobs:
+ evaluate:
+ if: github.repository_owner == 'ontrack-features-t2-2026'
+ runs-on: ubuntu-24.04
+ timeout-minutes: 5
+ environment:
+ name: ontrack-review-policy
+ deployment: false
+
+ steps:
+ - name: Check out the trusted policy evaluator
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
+ with:
+ ref: refs/heads/11.0.x
+ fetch-depth: 1
+ persist-credentials: false
+ sparse-checkout: .github/review-policy
+
+ - name: Verify policy logic
+ run: node --test .github/review-policy/evaluate.test.mjs
+
+ - name: Evaluate current pull-request approvals
+ env:
+ ONTRACK_REVIEW_APP_ID: ${{ vars.ONTRACK_REVIEW_APP_ID }}
+ ONTRACK_REVIEW_APP_PRIVATE_KEY: ${{ secrets.ONTRACK_REVIEW_APP_PRIVATE_KEY }}
+ ONTRACK_CONTRIBUTOR_TEAM: ontrack-contributors
+ ONTRACK_LEAD_TEAM: ontrack-leads
+ run: node .github/review-policy/evaluate.mjs
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000000..61cee19002
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,19 @@
+name: Test CI
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
+ - name: Use Node.js 22
+ uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci
+ - run: npm run test:ci
diff --git a/.github/workflows/weekly-integration-prs.yml b/.github/workflows/weekly-integration-prs.yml
new file mode 100644
index 0000000000..59eefb3cf4
--- /dev/null
+++ b/.github/workflows/weekly-integration-prs.yml
@@ -0,0 +1,100 @@
+name: Weekly integration PRs
+
+on:
+ schedule:
+ - cron: "17 9 * * 1"
+ timezone: Australia/Melbourne
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ ensure-pr:
+ if: github.repository_owner == 'ontrack-features-t2-2026'
+ name: ${{ matrix.source }} -> ${{ matrix.target }}
+ runs-on: ubuntu-24.04
+ timeout-minutes: 10
+
+ strategy:
+ fail-fast: false
+ max-parallel: 4
+ matrix:
+ include:
+ - source: feature/cross-unit
+ target: 11.0.x
+ - source: feature/notifications
+ target: 11.0.x
+ - source: feature/peer-progress-indicator
+ target: 11.0.x
+ - source: feature/calendar-integration
+ target: 11.0.x
+
+ concurrency:
+ group: ${{ github.workflow }}-${{ matrix.target }}-${{ matrix.source }}
+ cancel-in-progress: false
+
+ steps:
+ - name: Ensure pull request exists
+ shell: bash
+ env:
+ GH_TOKEN: ${{ secrets.INTEGRATION_BOT_TOKEN }}
+ SOURCE_BRANCH: ${{ matrix.source }}
+ TARGET_BRANCH: ${{ matrix.target }}
+ run: |
+ set -euo pipefail
+
+ if [[ -z "${GH_TOKEN:-}" ]]; then
+ echo "::error::INTEGRATION_BOT_TOKEN is not configured."
+ exit 1
+ fi
+
+ existing="$(
+ gh api --method GET "repos/$GITHUB_REPOSITORY/pulls" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2026-03-10" \
+ -f state=open \
+ -f head="${GITHUB_REPOSITORY_OWNER}:${SOURCE_BRANCH}" \
+ -f base="$TARGET_BRANCH" \
+ -F per_page=1 \
+ --jq '.[0].html_url // empty'
+ )"
+
+ if [[ -n "$existing" ]]; then
+ echo "Pull request already open: $existing"
+ exit 0
+ fi
+
+ target_ref="$(jq -rn --arg ref "$TARGET_BRANCH" '$ref | @uri')"
+ source_ref="$(jq -rn --arg ref "$SOURCE_BRANCH" '$ref | @uri')"
+
+ for ref in "$target_ref" "$source_ref"; do
+ if ! gh api "repos/$GITHUB_REPOSITORY/branches/${ref}" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2026-03-10" \
+ --silent >/dev/null 2>&1; then
+ echo "Branch $ref no longer exists in $GITHUB_REPOSITORY; skipping."
+ exit 0
+ fi
+ done
+ comparison="$(
+ gh api "repos/$GITHUB_REPOSITORY/compare/${target_ref}...${source_ref}" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2026-03-10" \
+ --jq '[.ahead_by, (.files | length)] | @tsv'
+ )"
+ read -r ahead changed_files <<< "$comparison"
+
+ if [[ "$ahead" == "0" || "$changed_files" == "0" ]]; then
+ echo "Nothing to merge from $SOURCE_BRANCH into $TARGET_BRANCH."
+ exit 0
+ fi
+
+ gh api --method POST "repos/$GITHUB_REPOSITORY/pulls" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2026-03-10" \
+ -f title="Integration: $SOURCE_BRANCH into $TARGET_BRANCH" \
+ -f head="$SOURCE_BRANCH" \
+ -f base="$TARGET_BRANCH" \
+ -f body="Created by the scheduled integration workflow. The source branch is intentionally retained after merge." \
+ --jq '.html_url'
diff --git a/.gitignore b/.gitignore
index 9abc43ca67..65909d007d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,10 +11,13 @@ vendor/
.sass-cache*
.bundle*
tmp.scss
-.vscode
.tscache
a.env
+.env
dist/
.angulardoc.json
.nx
.idea
+
+# Sentry Config File
+.sentryclirc
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000..f259c41cca
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "JPlag-Report-Viewer"]
+ path = JPlag-Report-Viewer
+ url = https://github.com/doubtfire-lms/jplag-report-viewer-static.git
diff --git a/.husky/commit-msg b/.husky/commit-msg
index fe4c17a22d..70bd3dd23d 100755
--- a/.husky/commit-msg
+++ b/.husky/commit-msg
@@ -1,4 +1 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
-npx --no-install commitlint --edit ""
+npx --no-install commitlint --edit "$1"
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 6700f51282..e69de29bb2 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,2 +0,0 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
diff --git a/.nvmrc b/.nvmrc
index 6276cf12fb..6bb900519d 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-v16.14.2
+v22.22.3
diff --git a/.postcssrc.json b/.postcssrc.json
new file mode 100644
index 0000000000..865e00b364
--- /dev/null
+++ b/.postcssrc.json
@@ -0,0 +1,6 @@
+{
+ "syntax": "postcss-scss",
+ "plugins": {
+ "@tailwindcss/postcss": {}
+ }
+}
diff --git a/.prettierignore b/.prettierignore
index 25257e586f..d541041bd2 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -4,3 +4,12 @@ yarn.lock
dist
build
e2e
+CHANGELOG.md
+JPlag-Report-Viewer/
+.github/
+karma/
+Gruntfile.js
+karma.conf.js
+polyfills.ts
+test.ts
+main.ts
diff --git a/.prettierrc b/.prettierrc
index d73829a932..021153e85a 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,9 +1,15 @@
{
"printWidth": 100,
"tabWidth": 2,
- "tabs": false,
"singleQuote": true,
- "semicolon": true,
+ "semi": true,
"quoteProps": "preserve",
- "bracketSpacing": false
+ "endOfLine": "lf",
+ "trailingComma": "all",
+ "bracketSpacing": false,
+ "plugins": ["@trivago/prettier-plugin-sort-imports"],
+ "importOrder": ["^@angular/(.*)$", "^rxjs$", "^rxjs/(.*)$", "^src/app/(.*)$", "^[./]"],
+ "importOrderSeparation": false,
+ "importOrderSortSpecifiers": true,
+ "importOrderParserPlugins": ["typescript", "decorators-legacy"]
}
diff --git a/.tool-versions b/.tool-versions
index c2ca3d3d25..42bb250e67 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1 +1 @@
-nodejs 20.9.0
+nodejs 22.22.3
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000..7e6882bfa3
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "files.eol": "\n"
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ca7d64128..52ce354d6d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,1840 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [11.0.0-45](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-44...v11.0.0-45) (2026-07-13)
+
+
+### Features
+
+* add label for starting tasks ([46100f5](https://github.com/b0ink/doubtfire-deploy/commit/46100f50ca8ddac11668e97deb482994f47f87f9))
+* add task list filters and sorting ([c65279c](https://github.com/b0ink/doubtfire-deploy/commit/c65279cb1900b5198e764d53b671719512486a83))
+* show all tasks in task planner ([eb87251](https://github.com/b0ink/doubtfire-deploy/commit/eb87251dbef37d5cca8d11c29816c42722605764))
+
+
+### Bug Fixes
+
+* dont reset task list sidebar when switching between project and task dashboard ([acc3ac2](https://github.com/b0ink/doubtfire-deploy/commit/acc3ac2625137075262c44239772d121e52c3a34))
+* lint ([dab681d](https://github.com/b0ink/doubtfire-deploy/commit/dab681d0e3d0e025804d3477eb68727ab42330ed))
+* use task weighting system as default sorting ([aa3c261](https://github.com/b0ink/doubtfire-deploy/commit/aa3c2613f9c1027eea761f51297380a4443a42b2))
+
+## [11.0.0-44](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-43...v11.0.0-44) (2026-07-08)
+
+
+### Features
+
+* display submitted grade in portfolio view ([a76cecf](https://github.com/b0ink/doubtfire-deploy/commit/a76cecfe1e4f3a78becf9507900e621e87447d69))
+
+
+### Bug Fixes
+
+* ensure accurate tutor views ([3b5e693](https://github.com/b0ink/doubtfire-deploy/commit/3b5e69397abcc53136942428fb97ac81a5cddb3d))
+* only call d2l mapping as convenor ([d6ecd44](https://github.com/b0ink/doubtfire-deploy/commit/d6ecd44658f6a668dde409d399944f53793c1c35))
+* persist portfolio filters in url ([322eef2](https://github.com/b0ink/doubtfire-deploy/commit/322eef29e57d13267a2466da4bcb4a55a7b49db6))
+* reduce threshold for mobile view in inbox dashboard ([9479351](https://github.com/b0ink/doubtfire-deploy/commit/9479351619479fed2ac26199f77946ac64903e7c))
+
+## [11.0.0-43](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-42...v11.0.0-43) (2026-07-03)
+
+
+### Bug Fixes
+
+* regenerate ngsw cache after sourcemaps removed ([be85d84](https://github.com/b0ink/doubtfire-deploy/commit/be85d8443b56f20a6866c0fc8d65915d5fe63a2a))
+
+## [11.0.0-42](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-41...v11.0.0-42) (2026-07-03)
+
+
+### Features
+
+* collapse task comments in narrow screen sizes ([380f277](https://github.com/b0ink/doubtfire-deploy/commit/380f2773b44d6582b62b13785b90a146a7002123))
+* render ansi output to html ([732aedd](https://github.com/b0ink/doubtfire-deploy/commit/732aedd2414dcfe3d74a78b4b9ca0bc17d4f5521))
+
+
+### Bug Fixes
+
+* add tooltip to collapsed task list item ([c81e61e](https://github.com/b0ink/doubtfire-deploy/commit/c81e61ef48b83f076ba7dac2d039b4bade5c9717))
+* ensure project is valid ([095315b](https://github.com/b0ink/doubtfire-deploy/commit/095315b212fff98cbe661df7a33a7b091cb66b1e))
+
+## [11.0.0-41](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-40...v11.0.0-41) (2026-07-02)
+
+
+### Bug Fixes
+
+* add padding when user cant view submission ([f16d9dc](https://github.com/b0ink/doubtfire-deploy/commit/f16d9dc6ee815e93022f9b8446dd32d1852ce088))
+* avoid repeated unit fetching ([cfe04dc](https://github.com/b0ink/doubtfire-deploy/commit/cfe04dc29f78ab815a17312f929e6deb052a88ed))
+* remove hash prefix ([51794b8](https://github.com/b0ink/doubtfire-deploy/commit/51794b800d2d731d6445cd21b5b592302e43defd))
+* tutorials route transition error ([db1294a](https://github.com/b0ink/doubtfire-deploy/commit/db1294a37e7333612202a8413256288ae97c05aa))
+
+## [11.0.0-40](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-39...v11.0.0-40) (2026-07-01)
+
+
+### Bug Fixes
+
+* reveal overflow task claims to convenors only ([c17a6d2](https://github.com/b0ink/doubtfire-deploy/commit/c17a6d239b71f446300b305de5877372c206028d))
+
+## [11.0.0-39](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-38...v11.0.0-39) (2026-07-01)
+
+
+### Bug Fixes
+
+* dynamic hint sizing to prevent overlap ([518b58b](https://github.com/b0ink/doubtfire-deploy/commit/518b58ba30c23dc4456a98ebfbe41cb48446e715))
+
+## [11.0.0-38](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-37...v11.0.0-38) (2026-06-29)
+
+## [11.0.0-37](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-36...v11.0.0-37) (2026-06-29)
+
+
+### Bug Fixes
+
+* fetch images on init ([b28bc69](https://github.com/b0ink/doubtfire-deploy/commit/b28bc6905b43d5843eee010cefff4938568edb10))
+
+## [11.0.0-36](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-35...v11.0.0-36) (2026-06-29)
+
+
+### Bug Fixes
+
+* modify grey border ([59c102d](https://github.com/b0ink/doubtfire-deploy/commit/59c102d7708e4c28a5584cc3614046e8e8f5fd52))
+
+## [11.0.0-35](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-34...v11.0.0-35) (2026-06-29)
+
+
+### Features
+
+* add skeleton loading ui to overseer image list ([b833a1c](https://github.com/b0ink/doubtfire-deploy/commit/b833a1c58ce07e6bf9b6a12c77a1bbea1635da10))
+* add skeleton ui for activities ([47ffe2b](https://github.com/b0ink/doubtfire-deploy/commit/47ffe2b261d26be39e93e7b89204c65b5d5b1f57))
+
+
+### Bug Fixes
+
+* reveal tii threshold if tii enabled ([ab1a401](https://github.com/b0ink/doubtfire-deploy/commit/ab1a401a3f67db83f7a55aa25060f2a29116eae1))
+* use correct api url ([ed4dba4](https://github.com/b0ink/doubtfire-deploy/commit/ed4dba4552ebf088893dfa58fffac2546690cedd))
+
+## [11.0.0-34](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-33...v11.0.0-34) (2026-06-25)
+
+
+### Bug Fixes
+
+* align icons ([8af5f5a](https://github.com/b0ink/doubtfire-deploy/commit/8af5f5ab177a8057e6b894ae7b039b8b8203c19f))
+* set correct button size ([ec8eef4](https://github.com/b0ink/doubtfire-deploy/commit/ec8eef49ca4275f00502872dab5b0cf23251c337))
+
+## [11.0.0-33](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-32...v11.0.0-33) (2026-06-25)
+
+
+### Bug Fixes
+
+* ensure session replays are captured ([bc35164](https://github.com/b0ink/doubtfire-deploy/commit/bc351643d65104ff85d716cff0e0d405cdb10170))
+
+## [11.0.0-32](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-31...v11.0.0-32) (2026-06-25)
+
+
+### Bug Fixes
+
+* ensure sentry replays work ([b2a8cdd](https://github.com/b0ink/doubtfire-deploy/commit/b2a8cdd2bcb15d409047c346b93136fd2f605b5f))
+
+## [11.0.0-31](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-30...v11.0.0-31) (2026-06-25)
+
+## [11.0.0-30](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-29...v11.0.0-30) (2026-06-25)
+
+
+### Bug Fixes
+
+* tunnel sentry requests ([2d24ebc](https://github.com/b0ink/doubtfire-deploy/commit/2d24ebc9e611dbf7ca0cbdcd8507b238538d9ea3))
+
+## [11.0.0-29](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-28...v11.0.0-29) (2026-06-25)
+
+
+### Features
+
+* sentry ([#1305](https://github.com/b0ink/doubtfire-deploy/issues/1305)) ([865faa2](https://github.com/b0ink/doubtfire-deploy/commit/865faa286016bf61fdb0ae0e300df48e92045bbb))
+
+## [11.0.0-28](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-27...v11.0.0-28) (2026-06-24)
+
+## [11.0.0-27](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-26...v11.0.0-27) (2026-06-24)
+
+
+### Features
+
+* customisable grades ([#1271](https://github.com/b0ink/doubtfire-deploy/issues/1271)) ([c2b68ac](https://github.com/b0ink/doubtfire-deploy/commit/c2b68aca0b5b0873443823795fc50bbcdc81fa4d))
+
+
+### Bug Fixes
+
+* allow empty strings to fix create user dialog ([efb4e5d](https://github.com/b0ink/doubtfire-deploy/commit/efb4e5d97f9641843f3e3e50f1fd38bb560fe853))
+* center div ([1c20dd7](https://github.com/b0ink/doubtfire-deploy/commit/1c20dd73befb5c0e204874140ca494c968487bd3))
+* close dialog after creating teachiing period ([af9ab4d](https://github.com/b0ink/doubtfire-deploy/commit/af9ab4d692f3e4c5e84a10e54a7f63d289a8092b))
+* ensure overseer assessment is valid before expanding ([6cad43c](https://github.com/b0ink/doubtfire-deploy/commit/6cad43c1c6aac6cf7552ed9db937d6a73cb17369))
+* ensure user is authenticated before loading scorm ([1b066c0](https://github.com/b0ink/doubtfire-deploy/commit/1b066c0e08cffc7a1176c7ec7f1b23da4ce6cf3f))
+* extend reply height to avoid text cutoff ([b4af5d1](https://github.com/b0ink/doubtfire-deploy/commit/b4af5d1b64dc32bea17b65c817aeaf56139be0fb))
+* hide quality pts if negative ([e43b96d](https://github.com/b0ink/doubtfire-deploy/commit/e43b96d77f21ebc654b4ca9e0b5a1cfeb5b8058c))
+* only show status icon if set ([46f494c](https://github.com/b0ink/doubtfire-deploy/commit/46f494c926b604f0c1fd396b59678f5ee74a7492))
+* truncate portfolio task list item correctly ([a02fe9e](https://github.com/b0ink/doubtfire-deploy/commit/a02fe9ebe57ae4157b9124d8a8a1c7aea364ba60))
+* update husky hooks for v9 ([5961860](https://github.com/b0ink/doubtfire-deploy/commit/596186040329ee198e1ef703d76676d15ad3ce89))
+* use correct scorm url ([d95c4ca](https://github.com/b0ink/doubtfire-deploy/commit/d95c4ca400a7aee7dd50f816e1b342e3754db87a))
+
+## [11.0.0-26](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-25...v11.0.0-26) (2026-06-19)
+
+
+### Bug Fixes
+
+* avoid converting colons in urls to emojis [#706](https://github.com/b0ink/doubtfire-deploy/issues/706) ([550b867](https://github.com/b0ink/doubtfire-deploy/commit/550b867df0e52026825e594a72d8544c9505a22b))
+* improve global loading logic for staff ([d81ac52](https://github.com/b0ink/doubtfire-deploy/commit/d81ac52cf1b90e139a34bf13ed08142919b6cc53))
+* prevent multiple global fetches ([f1528d1](https://github.com/b0ink/doubtfire-deploy/commit/f1528d1aedc7d6485b094ed3c4923b44c786d45b))
+
+## [11.0.0-25](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-24...v11.0.0-25) (2026-06-18)
+
+
+### Features
+
+* add day of week to gantt chart ([1fb4637](https://github.com/b0ink/doubtfire-deploy/commit/1fb4637cd2386f1aeff227043e0002bf9b77837b))
+* engagement passport ([#1257](https://github.com/b0ink/doubtfire-deploy/issues/1257)) ([7a754dc](https://github.com/b0ink/doubtfire-deploy/commit/7a754dcc82ca228f5688abab4480774df75a007e))
+* show students name if viewing other project ([38c0e85](https://github.com/b0ink/doubtfire-deploy/commit/38c0e85ebe698c71ea087f3aeecc6d7ca24c86d7))
+
+
+### Bug Fixes
+
+* ensure dashboard dropdown switches from task details view ([1ba5d1e](https://github.com/b0ink/doubtfire-deploy/commit/1ba5d1e42d302218520f8be4c0875673ad5d7a3f))
+* web calendar ([#1282](https://github.com/b0ink/doubtfire-deploy/issues/1282)) ([83b6d5e](https://github.com/b0ink/doubtfire-deploy/commit/83b6d5ea381604ae88585195314eafd239880632))
+
+## [11.0.0-24](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-23...v11.0.0-24) (2026-06-17)
+
+
+### Features
+
+* check access token expiry locally before attempting request ([#1270](https://github.com/b0ink/doubtfire-deploy/issues/1270)) ([4ff5562](https://github.com/b0ink/doubtfire-deploy/commit/4ff55627566f9004872c915658bd91bc348dd8e8))
+* submission history ([#1269](https://github.com/b0ink/doubtfire-deploy/issues/1269)) ([4cdae07](https://github.com/b0ink/doubtfire-deploy/commit/4cdae07eb5bef0f9866927892e92b6b80dde0c74))
+* upgrade gantt chart and add screenshotting ability ([#1263](https://github.com/b0ink/doubtfire-deploy/issues/1263)) ([08a8eee](https://github.com/b0ink/doubtfire-deploy/commit/08a8eee9337331aa328960e206722f8905c2a0f9))
+
+
+### Bug Fixes
+
+* use either portfolio available field ([f8d5142](https://github.com/b0ink/doubtfire-deploy/commit/f8d514261c7eca5465ac3af95da33b95a39d3f23))
+
+## [11.0.0-23](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-22...v11.0.0-23) (2026-06-11)
+
+
+### Features
+
+* display list of tasks still being processed ([#1264](https://github.com/b0ink/doubtfire-deploy/issues/1264)) ([55ef4c0](https://github.com/b0ink/doubtfire-deploy/commit/55ef4c0409cf5a066e91763a00f2bb428670e520))
+
+
+### Bug Fixes
+
+* apply status color ([df6a6ec](https://github.com/b0ink/doubtfire-deploy/commit/df6a6ecc73d01dd2ef449270bc7c6de446d93884))
+* avoid loading rendering all students at the same time ([ba7910a](https://github.com/b0ink/doubtfire-deploy/commit/ba7910a59460a4d4139273b272e99625e5efc518))
+* ensure entire task list can be scrolled through ([8ce79a8](https://github.com/b0ink/doubtfire-deploy/commit/8ce79a8f33c82231f7f3c4033a4d37039514c015))
+
+## [11.0.0-22](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-21...v11.0.0-22) (2026-06-09)
+
+
+### Features
+
+* communications system ([#1239](https://github.com/b0ink/doubtfire-deploy/issues/1239)) ([b55bcfc](https://github.com/b0ink/doubtfire-deploy/commit/b55bcfc8f84f381beb7d62f41031db3ba6a38193))
+
+## [11.0.0-21](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-20...v11.0.0-21) (2026-06-08)
+
+
+### Bug Fixes
+
+* ensure unit dates map correctly ([532f185](https://github.com/b0ink/doubtfire-deploy/commit/532f185d2bc6839549a06a2f9e625c676cc280a6))
+
+## [11.0.0-20](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-19...v11.0.0-20) (2026-06-04)
+
+## [11.0.0-19](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-18...v11.0.0-19) (2026-06-03)
+
+
+### Bug Fixes
+
+* ensure unit is fetched in unit task editor ([54b1cf0](https://github.com/b0ink/doubtfire-deploy/commit/54b1cf03ed662447e7027a61d084f62f2548c996))
+
+## [11.0.0-18](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-17...v11.0.0-18) (2026-06-03)
+
+## [11.0.0-17](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-16...v11.0.0-17) (2026-06-03)
+
+
+### Features
+
+* support zip file submissions ([#1240](https://github.com/b0ink/doubtfire-deploy/issues/1240)) ([99d546f](https://github.com/b0ink/doubtfire-deploy/commit/99d546f1544feeab31fc6b2434e330b22544b56f))
+
+## [11.0.0-16](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-15...v11.0.0-16) (2026-06-03)
+
+## [11.0.0-15](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-14...v11.0.0-15) (2026-06-03)
+
+
+### Bug Fixes
+
+* display past due date on same day ([cd47f5c](https://github.com/b0ink/doubtfire-deploy/commit/cd47f5c0e4c351e2421afd94647a1297b3b5ec75))
+* prevent reload of task list component when switching back to dashboard ([be780b1](https://github.com/b0ink/doubtfire-deploy/commit/be780b14bc875c05e1083353c6d447315bc179e7))
+
+## [11.0.0-14](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-13...v11.0.0-14) (2026-06-03)
+
+## [11.0.0-13](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-12...v11.0.0-13) (2026-06-03)
+
+## [11.0.0-12](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-11...v11.0.0-12) (2026-06-02)
+
+
+### Bug Fixes
+
+* correctly apply task tutorial filters ([924c267](https://github.com/b0ink/doubtfire-deploy/commit/924c2676c6739820cc066d046b7ace71ee2d2f9e))
+
+## [11.0.0-11](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-10...v11.0.0-11) (2026-05-19)
+
+## [11.0.0-10](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-9...v11.0.0-10) (2026-05-18)
+
+## [11.0.0-9](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-8...v11.0.0-9) (2026-05-18)
+
+
+### Bug Fixes
+
+* ensure unit is loaded first before querying inbox ([6d7c9fb](https://github.com/b0ink/doubtfire-deploy/commit/6d7c9fb8ff7fe714844ebc8f0e04f2ef9a40d4d0))
+
+## [11.0.0-8](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-7...v11.0.0-8) (2026-05-18)
+
+## [11.0.0-7](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-6...v11.0.0-7) (2026-05-17)
+
+## [11.0.0-6](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-5...v11.0.0-6) (2026-05-14)
+
+## [11.0.0-5](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-4...v11.0.0-5) (2026-05-14)
+
+
+### Bug Fixes
+
+* ensure access to tutor notes ([5e0ec7c](https://github.com/b0ink/doubtfire-deploy/commit/5e0ec7cf6ccf04286aa6865e5d84d14487f68f7f))
+* ensure valid selected task ([deaae7f](https://github.com/b0ink/doubtfire-deploy/commit/deaae7fb819ea8dfc01d6ad67415f50c2e4258a2))
+
+## [11.0.0-4](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-3...v11.0.0-4) (2026-05-14)
+
+
+### Features
+
+* add route auth guards ([fb2e781](https://github.com/b0ink/doubtfire-deploy/commit/fb2e78113b13782dbbafe38c21f27c5d63be69c6))
+
+
+### Bug Fixes
+
+* enable inbox access for tutors ([05d3eda](https://github.com/b0ink/doubtfire-deploy/commit/05d3eda76bdcfb491649f5d83e69213db3de0885))
+
+## [11.0.0-3](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-2...v11.0.0-3) (2026-05-14)
+
+
+### Bug Fixes
+
+* add padding ([94f4216](https://github.com/b0ink/doubtfire-deploy/commit/94f4216e588d6dfb6abc4d10804971cb0e8f8678))
+* unlock task status selection for staff ([#1222](https://github.com/b0ink/doubtfire-deploy/issues/1222)) ([e4080d0](https://github.com/b0ink/doubtfire-deploy/commit/e4080d03a629654518bc362f48e8e0fbb790dd96))
+
+## [11.0.0-2](https://github.com/b0ink/doubtfire-deploy/compare/v11.0.0-1...v11.0.0-2) (2026-05-13)
+
+## [11.0.0-1](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-21...v11.0.0-1) (2026-05-13)
+
+
+### Features
+
+* (wip) add name to new student experience skeleton ([7b9ace4](https://github.com/b0ink/doubtfire-deploy/commit/7b9ace47132f0237bc621d3d0e45c3031e7fac05))
+* add burndown replacement ([837658a](https://github.com/b0ink/doubtfire-deploy/commit/837658afff090e52d53cefa7c2eed20a46c1ac01))
+* add new progress component ([8beef70](https://github.com/b0ink/doubtfire-deploy/commit/8beef701e6b7fc0dbea42f8849dc02e79a665fb7))
+* add new student experience ([9bb77ab](https://github.com/b0ink/doubtfire-deploy/commit/9bb77ab108e50b637329bd739925d1d9566d9203))
+* allow paste attachment comment ([#1165](https://github.com/b0ink/doubtfire-deploy/issues/1165)) ([0f24980](https://github.com/b0ink/doubtfire-deploy/commit/0f24980e6edcc5d0f81e015990adaf14afea400e))
+* batch upload feedback csv ([#1175](https://github.com/b0ink/doubtfire-deploy/issues/1175)) ([9f95987](https://github.com/b0ink/doubtfire-deploy/commit/9f959877b6f47d878d87b468f4aab73f536d598b))
+* bulk import staff via emails ([#1195](https://github.com/b0ink/doubtfire-deploy/issues/1195)) ([c91ab47](https://github.com/b0ink/doubtfire-deploy/commit/c91ab478ae1d1cd459040290f6f7a44c7dce3efe))
+* confirm recursive fix in mobile tutor view ([b64601a](https://github.com/b0ink/doubtfire-deploy/commit/b64601a425c7a64bccfaf47c1e4fccb0343b1589))
+* confirmation modal to reassign tutorials when removing staff ([5f90e90](https://github.com/b0ink/doubtfire-deploy/commit/5f90e9085d69913eced55ad54ce9ac09431d3822))
+* discussed in class refactor ([#1145](https://github.com/b0ink/doubtfire-deploy/issues/1145)) ([4d8bb5b](https://github.com/b0ink/doubtfire-deploy/commit/4d8bb5b82d89caa02ed4936819be601b3f6977fc))
+* display icon for tasks escalated by student ([dfcfd47](https://github.com/b0ink/doubtfire-deploy/commit/dfcfd472305ef2971fa72734c18e77253b98fa11))
+* display portfolio submission time ([d717b27](https://github.com/b0ink/doubtfire-deploy/commit/d717b270eb80c0b0245e6b39f0b3f0567c379512))
+* display sso redirecting state ([248c992](https://github.com/b0ink/doubtfire-deploy/commit/248c992f46488f61916e00a87ca32ed987721f31))
+* edit comments ([#1194](https://github.com/b0ink/doubtfire-deploy/issues/1194)) ([976b6ac](https://github.com/b0ink/doubtfire-deploy/commit/976b6ac4fa3bd2d5e954eebcae3fcb40dd8d1f0e))
+* enable task pinning in explorer ([1647e2b](https://github.com/b0ink/doubtfire-deploy/commit/1647e2bcc86ac6fb08c82be9795a06939c57bb6e))
+* improve look of task status count ([475c316](https://github.com/b0ink/doubtfire-deploy/commit/475c3165db099e1412e30fb6cf18bbaddd516e75))
+* pause feedback threshold during teaching period breaks ([#1138](https://github.com/b0ink/doubtfire-deploy/issues/1138)) ([12fbf81](https://github.com/b0ink/doubtfire-deploy/commit/12fbf8147107dc185a9a9cbea940efac93a0f316))
+* require discussion before marking complete ([#1103](https://github.com/b0ink/doubtfire-deploy/issues/1103)) ([86ae886](https://github.com/b0ink/doubtfire-deploy/commit/86ae8865268ee19e4b3430ff679358cc075e1346))
+* staff note and similarity indicators ([4a65a9b](https://github.com/b0ink/doubtfire-deploy/commit/4a65a9b21d615ecf78bf09743c9cf8c3026ff621))
+* visualisations ([f1cc39e](https://github.com/b0ink/doubtfire-deploy/commit/f1cc39e59dd224c55ee0e8b5f1d50855263d147e))
+
+
+### Bug Fixes
+
+* avoid rendering the staff list twice ([9e5be59](https://github.com/b0ink/doubtfire-deploy/commit/9e5be591b9a0cf72d01efa5e28e5b2b9e7eba043))
+* burndown chart visualisation ([#942](https://github.com/b0ink/doubtfire-deploy/issues/942)) ([8487b8d](https://github.com/b0ink/doubtfire-deploy/commit/8487b8d20e838fa1b2a2238bb856f73edc86442c))
+* check for valid unit ([d9560c3](https://github.com/b0ink/doubtfire-deploy/commit/d9560c3b68df0a09f577cdab990d252b5cd24c58))
+* complete student enrolment modal ([efa89c3](https://github.com/b0ink/doubtfire-deploy/commit/efa89c344069d386f2411a417e570d4158af91b9))
+* debounce duplicate task submission requests ([80f92e2](https://github.com/b0ink/doubtfire-deploy/commit/80f92e2277c48978e1738340063a7223c04fddb8))
+* display groups only when a group set is selected ([dde1e76](https://github.com/b0ink/doubtfire-deploy/commit/dde1e7697f8a9a93577bb0e18e3e81a3ebd1ace2))
+* duplicate files ([da51e8e](https://github.com/b0ink/doubtfire-deploy/commit/da51e8e0cc9518a2a76447a48079b42f144064fc))
+* ensure authorisation active in angular ([deaa1e9](https://github.com/b0ink/doubtfire-deploy/commit/deaa1e940f8295ff962c111f1dbac75ed5ff51fa))
+* ensure loading screen removed in sign in component ([6954ac6](https://github.com/b0ink/doubtfire-deploy/commit/6954ac62627e058b6a73c0e83dc8dc8a1740698d))
+* ensure pdf viewer is visible [#1186](https://github.com/b0ink/doubtfire-deploy/issues/1186) ([6bc0752](https://github.com/b0ink/doubtfire-deploy/commit/6bc075228fe2a48dcf91aa5350b5f15f602e2dc8))
+* ensure selected group is valid ([95cb9ac](https://github.com/b0ink/doubtfire-deploy/commit/95cb9ace6615b45dd75161fd02d6dd130420817b))
+* fix pdf viewer for portfolios ([456cf46](https://github.com/b0ink/doubtfire-deploy/commit/456cf466bf49b86467b269ec3d6fb4e63f6a059f))
+* get new visualisations to build ([f828fd4](https://github.com/b0ink/doubtfire-deploy/commit/f828fd42fafaf09d7f807cea8f6a1e14ec56ecc5))
+* link task list to definitions for project dashboard ([90853e9](https://github.com/b0ink/doubtfire-deploy/commit/90853e93c109430669e3f80e4324649cd2d0dba6))
+* new burndown and task status count ([bfecd08](https://github.com/b0ink/doubtfire-deploy/commit/bfecd086eeeae33e9bbba430c79bcc4f21506a07))
+* only render if submission date is valid ([04986a0](https://github.com/b0ink/doubtfire-deploy/commit/04986a0e501f0185dbd199b8d2554133217f7e75))
+* open report in turnitin ([091aaf8](https://github.com/b0ink/doubtfire-deploy/commit/091aaf8ba744bbf677f9b8f51a58bc16b7d631ab))
+* remove hardcoded chart view size ([1b31930](https://github.com/b0ink/doubtfire-deploy/commit/1b319303ac969ef63617b2362297233bc79bb916))
+* remove markdown filter from learning outcomes ([1de217c](https://github.com/b0ink/doubtfire-deploy/commit/1de217c0bc19b8dbedb21cf2e89e195772ae2a02))
+* set task data for project dashboard state ([ef75340](https://github.com/b0ink/doubtfire-deploy/commit/ef7534064ef92c06e459953076bc30b6eff4647d))
+* support building on windows ([b2aa7ae](https://github.com/b0ink/doubtfire-deploy/commit/b2aa7ae6ea7a456953ed4f2a8c63f784ac3870b5))
+* switch staff to unit roles in unit service ([0e5d9de](https://github.com/b0ink/doubtfire-deploy/commit/0e5d9de9df2eaf16e473f4f8fde920ffb59ce765))
+* task route transition race when switching from inbox ([63c52dc](https://github.com/b0ink/doubtfire-deploy/commit/63c52dc4cdb2f2fbc701f69c64b7273f5778c868))
+
+### [10.0.1-35](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-34...v10.0.1-35) (2026-04-28)
+
+
+### Features
+
+* bulk import staff via emails ([#1195](https://github.com/b0ink/doubtfire-deploy/issues/1195)) ([c91ab47](https://github.com/b0ink/doubtfire-deploy/commit/c91ab478ae1d1cd459040290f6f7a44c7dce3efe))
+* edit comments ([#1194](https://github.com/b0ink/doubtfire-deploy/issues/1194)) ([976b6ac](https://github.com/b0ink/doubtfire-deploy/commit/976b6ac4fa3bd2d5e954eebcae3fcb40dd8d1f0e))
+
+
+### Bug Fixes
+
+* task route transition race when switching from inbox ([63c52dc](https://github.com/b0ink/doubtfire-deploy/commit/63c52dc4cdb2f2fbc701f69c64b7273f5778c868))
+
+### [10.0.1-34](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-33...v10.0.1-34) (2026-04-27)
+
+### [10.0.1-33](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-32...v10.0.1-33) (2026-04-23)
+
+
+### Features
+
+* allow paste attachment comment ([#1165](https://github.com/b0ink/doubtfire-deploy/issues/1165)) ([0f24980](https://github.com/b0ink/doubtfire-deploy/commit/0f24980e6edcc5d0f81e015990adaf14afea400e))
+
+
+### Bug Fixes
+
+* open report in turnitin ([091aaf8](https://github.com/b0ink/doubtfire-deploy/commit/091aaf8ba744bbf677f9b8f51a58bc16b7d631ab))
+
+### [10.0.1-32](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-31...v10.0.1-32) (2026-04-18)
+
+### [10.0.1-31](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-30...v10.0.1-31) (2026-04-18)
+
+### [10.0.1-30](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-29...v10.0.1-30) (2026-04-18)
+
+
+### Features
+
+* batch upload feedback csv ([#1175](https://github.com/b0ink/doubtfire-deploy/issues/1175)) ([9f95987](https://github.com/b0ink/doubtfire-deploy/commit/9f959877b6f47d878d87b468f4aab73f536d598b))
+* display icon for tasks escalated by student ([dfcfd47](https://github.com/b0ink/doubtfire-deploy/commit/dfcfd472305ef2971fa72734c18e77253b98fa11))
+* enable task pinning in explorer ([1647e2b](https://github.com/b0ink/doubtfire-deploy/commit/1647e2bcc86ac6fb08c82be9795a06939c57bb6e))
+
+
+### Bug Fixes
+
+* debounce duplicate task submission requests ([80f92e2](https://github.com/b0ink/doubtfire-deploy/commit/80f92e2277c48978e1738340063a7223c04fddb8))
+
+### [10.0.1-29](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-28...v10.0.1-29) (2026-04-15)
+
+
+### Features
+
+* discussed in class refactor ([#1145](https://github.com/b0ink/doubtfire-deploy/issues/1145)) ([4d8bb5b](https://github.com/b0ink/doubtfire-deploy/commit/4d8bb5b82d89caa02ed4936819be601b3f6977fc))
+
+### [10.0.1-28](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-27...v10.0.1-28) (2026-04-13)
+
+### [10.0.1-27](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.27...v10.0.1-27) (2026-04-13)
+
+### [10.0.27](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-26...v10.0.27) (2026-04-13)
+
+
+### Features
+
+* confirmation modal to reassign tutorials when removing staff ([5f90e90](https://github.com/b0ink/doubtfire-deploy/commit/5f90e9085d69913eced55ad54ce9ac09431d3822))
+
+### [10.0.1-26](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-25...v10.0.1-26) (2026-03-26)
+
+### [10.0.1-25](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-24...v10.0.1-25) (2026-03-26)
+
+
+### Features
+
+* display sso redirecting state ([248c992](https://github.com/b0ink/doubtfire-deploy/commit/248c992f46488f61916e00a87ca32ed987721f31))
+* pause feedback threshold during teaching period breaks ([#1138](https://github.com/b0ink/doubtfire-deploy/issues/1138)) ([12fbf81](https://github.com/b0ink/doubtfire-deploy/commit/12fbf8147107dc185a9a9cbea940efac93a0f316))
+
+### [10.0.1-24](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-23...v10.0.1-24) (2026-03-24)
+
+
+### Features
+
+* confirm recursive fix in mobile tutor view ([b64601a](https://github.com/b0ink/doubtfire-deploy/commit/b64601a425c7a64bccfaf47c1e4fccb0343b1589))
+
+### [10.0.1-23](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-22...v10.0.1-23) (2026-03-24)
+
+### [10.0.1-22](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-21...v10.0.1-22) (2026-03-23)
+
+
+### Features
+
+* display portfolio submission time ([d717b27](https://github.com/b0ink/doubtfire-deploy/commit/d717b270eb80c0b0245e6b39f0b3f0567c379512))
+* require discussion before marking complete ([#1103](https://github.com/b0ink/doubtfire-deploy/issues/1103)) ([86ae886](https://github.com/b0ink/doubtfire-deploy/commit/86ae8865268ee19e4b3430ff679358cc075e1346))
+
+
+### Bug Fixes
+
+* avoid rendering the staff list twice ([9e5be59](https://github.com/b0ink/doubtfire-deploy/commit/9e5be591b9a0cf72d01efa5e28e5b2b9e7eba043))
+* only render if submission date is valid ([04986a0](https://github.com/b0ink/doubtfire-deploy/commit/04986a0e501f0185dbd199b8d2554133217f7e75))
+
+### [10.0.1-21](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-20...v10.0.1-21) (2026-03-13)
+
+### [10.0.1-20](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-19...v10.0.1-20) (2026-03-13)
+
+### [10.0.1-19](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-18...v10.0.1-19) (2026-03-13)
+
+
+### Features
+
+* view submission history + zip file editor ([#1104](https://github.com/b0ink/doubtfire-deploy/issues/1104)) ([d706f09](https://github.com/b0ink/doubtfire-deploy/commit/d706f0918285a2c6d69c6240a93b41e88506f771))
+
+### [10.0.1-18](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-17...v10.0.1-18) (2026-03-11)
+
+
+### Bug Fixes
+
+* convert emojis in submission comment to string with colons ([#1105](https://github.com/b0ink/doubtfire-deploy/issues/1105)) ([7c508cb](https://github.com/b0ink/doubtfire-deploy/commit/7c508cba8695fcd7b6df4aaa53a70066ecb137f3))
+* dont subtract tutor note count for reading your own notes ([8fe6171](https://github.com/b0ink/doubtfire-deploy/commit/8fe61718c0e263fa965b861763337af27c175b36))
+
+### [10.0.1-17](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-16...v10.0.1-17) (2026-03-06)
+
+
+### Bug Fixes
+
+* empty prompts layout ([c77325a](https://github.com/b0ink/doubtfire-deploy/commit/c77325a2992d0b326bdeb27cd00a955aaa19811c))
+
+### [10.0.1-16](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-15...v10.0.1-16) (2026-03-04)
+
+### [10.0.1-15](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-14...v10.0.1-15) (2026-03-04)
+
+### [10.0.1-14](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-13...v10.0.1-14) (2026-03-03)
+
+
+### Features
+
+* view tutor notes from unit staff editor ([011980d](https://github.com/b0ink/doubtfire-deploy/commit/011980ddebb4caddaebb380aae4825930c077122))
+
+### [10.0.1-13](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-12...v10.0.1-13) (2026-03-02)
+
+
+### Features
+
+* add snooze moderation action ([#1096](https://github.com/b0ink/doubtfire-deploy/issues/1096)) ([bdcefda](https://github.com/b0ink/doubtfire-deploy/commit/bdcefda81e4acdef56ebe9f228632f26a904287c))
+
+### [10.0.1-12](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-11...v10.0.1-12) (2026-03-02)
+
+### [10.0.1-11](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-10...v10.0.1-11) (2026-02-28)
+
+
+### Bug Fixes
+
+* ensure deadline is returned ([6629a5f](https://github.com/b0ink/doubtfire-deploy/commit/6629a5f4b9834009dabf19a60a36c1289126034d))
+
+### [10.0.1-10](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-9...v10.0.1-10) (2026-02-26)
+
+
+### Features
+
+* display available storage for docker images ([#1092](https://github.com/b0ink/doubtfire-deploy/issues/1092)) ([81ebd34](https://github.com/b0ink/doubtfire-deploy/commit/81ebd3488d20a1e057c24b37190f4eea99bad9ec))
+
+### [10.0.1-9](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-8...v10.0.1-9) (2026-02-25)
+
+
+### Bug Fixes
+
+* typo ([4ecfe27](https://github.com/b0ink/doubtfire-deploy/commit/4ecfe27c91104c746ac8a4f9059030d5475776bb))
+
+### [10.0.1-8](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-7...v10.0.1-8) (2026-02-25)
+
+
+### Features
+
+* overflow marking ([#1086](https://github.com/b0ink/doubtfire-deploy/issues/1086)) ([e047988](https://github.com/b0ink/doubtfire-deploy/commit/e047988c4fff787852d1cc5a041cb48e11ee57f8))
+
+### [10.0.1-7](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-6...v10.0.1-7) (2026-02-25)
+
+
+### Bug Fixes
+
+* ensure timestamp is normalised to start of day ([671c391](https://github.com/b0ink/doubtfire-deploy/commit/671c3916797dc5ab680b9dac52ae68626e49d351))
+
+### [10.0.1-6](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-5...v10.0.1-6) (2026-02-24)
+
+### [10.0.1-5](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-4...v10.0.1-5) (2026-02-24)
+
+
+### Features
+
+* per grade start dates ([#1090](https://github.com/b0ink/doubtfire-deploy/issues/1090)) ([a35019a](https://github.com/b0ink/doubtfire-deploy/commit/a35019aa4011fce7808da8cc1481fae7635aac69))
+
+### [10.0.1-4](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-3...v10.0.1-4) (2026-02-23)
+
+
+### Features
+
+* allow custom target dates per target grade ([#1089](https://github.com/b0ink/doubtfire-deploy/issues/1089)) ([79b5af6](https://github.com/b0ink/doubtfire-deploy/commit/79b5af6bf05419e05dbef69e6ef1f72bfc85097c))
+
+### [10.0.1-3](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-2...v10.0.1-3) (2026-02-21)
+
+### [10.0.1-2](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-1...v10.0.1-2) (2026-02-19)
+
+
+### Bug Fixes
+
+* set min width for mentor dropdown ([75cc1b3](https://github.com/b0ink/doubtfire-deploy/commit/75cc1b3569d17cd54e71f86874addb4bc34d566f))
+
+### [10.0.1-1](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.1-0...v10.0.1-1) (2026-02-18)
+
+
+### Features
+
+* tutor notes + mentorship + moderation + escalation ([#1068](https://github.com/b0ink/doubtfire-deploy/issues/1068)) ([48f4db7](https://github.com/b0ink/doubtfire-deploy/commit/48f4db730f06e349f5e3c791f1972d23b0f8573f))
+
+
+### Bug Fixes
+
+* ensure tutor notes view extends full height ([c872ed7](https://github.com/b0ink/doubtfire-deploy/commit/c872ed766bf493456da56e33dcda48a90ed3f6ce))
+
+### [10.0.1-0](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0...v10.0.1-0) (2026-02-17)
+
+## [10.0.0](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-73...v10.0.0) (2026-02-12)
+
+## [10.0.0-73](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-72...v10.0.0-73) (2026-02-12)
+
+## [10.0.0-72](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-71...v10.0.0-72) (2026-02-12)
+
+
+### Bug Fixes
+
+* null safe user access ([f6aeecc](https://github.com/b0ink/doubtfire-deploy/commit/f6aeecc6c2c79909ca859422e7d9b3bc65ef6456))
+
+## [10.0.0-71](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-70...v10.0.0-71) (2026-02-11)
+
+
+### Features
+
+* download staff notes csv ([#1062](https://github.com/b0ink/doubtfire-deploy/issues/1062)) ([6dcfe95](https://github.com/b0ink/doubtfire-deploy/commit/6dcfe952f0415fb1327b738137f817a0fcfa0488))
+* migrate to jplag report viewer v6.3.0 ([#1082](https://github.com/b0ink/doubtfire-deploy/issues/1082)) ([697588b](https://github.com/b0ink/doubtfire-deploy/commit/697588b98e28ec663f4982c49f18fe8109ba7c34))
+
+## [10.0.0-70](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-69...v10.0.0-70) (2026-01-05)
+
+
+### Bug Fixes
+
+* skip query for unsaved task definition ([a6bdd22](https://github.com/b0ink/doubtfire-deploy/commit/a6bdd22ff864e72c679fae1844345963447fdf08))
+
+## [10.0.0-69](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-68...v10.0.0-69) (2026-01-05)
+
+
+### Features
+
+* attention required task status ([#1061](https://github.com/b0ink/doubtfire-deploy/issues/1061)) ([15365ed](https://github.com/b0ink/doubtfire-deploy/commit/15365ed4f42af790af9a9ae24dae42f041dff1df))
+* display number of stuff notes in tutor discussion ([2674d4e](https://github.com/b0ink/doubtfire-deploy/commit/2674d4e5ade26e5420b87518f4cd9da909ec7246))
+* overseer pipeline ([#1064](https://github.com/b0ink/doubtfire-deploy/issues/1064)) ([7cb8fc7](https://github.com/b0ink/doubtfire-deploy/commit/7cb8fc7471f0402c562fb8a0b87df672ceb22ea3))
+* project task planner gantt chart ([#1051](https://github.com/b0ink/doubtfire-deploy/issues/1051)) ([4f60792](https://github.com/b0ink/doubtfire-deploy/commit/4f607926e3ac305316a05607fda7f7bff7faec00))
+
+## [10.0.0-68](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-67...v10.0.0-68) (2025-12-08)
+
+
+### Bug Fixes
+
+* only trim if valid ([fbc63b0](https://github.com/b0ink/doubtfire-deploy/commit/fbc63b0308d87d921e1d74788e8da6031443e57e))
+
+## [10.0.0-67](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-66...v10.0.0-67) (2025-12-03)
+
+
+### Features
+
+* allow sidekiq web access ([#1053](https://github.com/b0ink/doubtfire-deploy/issues/1053)) ([6651279](https://github.com/b0ink/doubtfire-deploy/commit/6651279c49f431dbddf6fd5d810440ae7b3b9898))
+
+## [10.0.0-66](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-65...v10.0.0-66) (2025-12-03)
+
+
+### Features
+
+* discussion prompts ([#1042](https://github.com/b0ink/doubtfire-deploy/issues/1042)) ([730ffd0](https://github.com/b0ink/doubtfire-deploy/commit/730ffd0c3f79a5ad42c0c59c2f2c2a901ab0d702))
+
+
+### Bug Fixes
+
+* correctly set rollover end date ([0611be0](https://github.com/b0ink/doubtfire-deploy/commit/0611be04e8fe7e75ae07504945a2984839475e3a))
+
+## [10.0.0-65](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-64...v10.0.0-65) (2025-11-25)
+
+
+### Bug Fixes
+
+* typo ([bdcd75d](https://github.com/b0ink/doubtfire-deploy/commit/bdcd75d91d5ddc7d9a60317bd91289a7242251e1))
+
+## [10.0.0-64](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-63...v10.0.0-64) (2025-11-25)
+
+
+### Bug Fixes
+
+* avoid calling window to open project in new tab ([9ab015d](https://github.com/b0ink/doubtfire-deploy/commit/9ab015d168cf7e421500910766b9e6fb0bf907f5))
+* use new google fonts api for proper weight loading ([#1039](https://github.com/b0ink/doubtfire-deploy/issues/1039)) ([40f2640](https://github.com/b0ink/doubtfire-deploy/commit/40f2640e6579536cef8e82e9476733083b82bac1))
+
+## [10.0.0-63](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-62...v10.0.0-63) (2025-11-10)
+
+
+### Features
+
+* open project dashboard from portfolios view ([5b4b097](https://github.com/b0ink/doubtfire-deploy/commit/5b4b097a9b189e5a816695cce7438104f1144283)), closes [#1040](https://github.com/b0ink/doubtfire-deploy/issues/1040)
+
+## [10.0.0-62](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-61...v10.0.0-62) (2025-11-06)
+
+
+### Features
+
+* restrict assessments to tutors in the same tutorial stream ([#1033](https://github.com/b0ink/doubtfire-deploy/issues/1033)) ([29fbc14](https://github.com/b0ink/doubtfire-deploy/commit/29fbc1498b0d48811cb59c5f2a66871fbac06849))
+
+## [10.0.0-61](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-60...v10.0.0-61) (2025-11-06)
+
+## [10.0.0-60](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-59...v10.0.0-60) (2025-11-05)
+
+
+### Features
+
+* upload grades csv ([#1038](https://github.com/b0ink/doubtfire-deploy/issues/1038)) ([c67ceaa](https://github.com/b0ink/doubtfire-deploy/commit/c67ceaa84d02bb71626890fc28bb4aaea164b952))
+
+## [10.0.0-59](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-58...v10.0.0-59) (2025-11-04)
+
+
+### Bug Fixes
+
+* require comment for new evidence ([58ec499](https://github.com/b0ink/doubtfire-deploy/commit/58ec499a8d59c7202f591de661551d8a32c37527))
+* require comment for new evidence ([c5397fc](https://github.com/b0ink/doubtfire-deploy/commit/c5397fc5c444e76b431aa5d6487553c60e711c2e))
+
+## [10.0.0-58](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-57...v10.0.0-58) (2025-11-01)
+
+## [10.0.0-57](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-56...v10.0.0-57) (2025-10-30)
+
+
+### Features
+
+* open similarities from project dashboard ([#1035](https://github.com/b0ink/doubtfire-deploy/issues/1035)) ([ec0af0c](https://github.com/b0ink/doubtfire-deploy/commit/ec0af0cbe47392d6542d9741e1131954195d407f))
+
+
+### Bug Fixes
+
+* enable submission button for 'need help' trigger ([#1034](https://github.com/b0ink/doubtfire-deploy/issues/1034)) ([07de5b1](https://github.com/b0ink/doubtfire-deploy/commit/07de5b1e34f78f83c1418399f45e8c754aea530a))
+* typo ([2712a68](https://github.com/b0ink/doubtfire-deploy/commit/2712a68f6031794c571a7ee5866c653bf67de1dd))
+
+## [10.0.0-56](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-55...v10.0.0-56) (2025-10-23)
+
+
+### Bug Fixes
+
+* ensure grade has been selected ([32e03d1](https://github.com/b0ink/doubtfire-deploy/commit/32e03d111e61df42a9c67cb9e68d271f9300d5bc))
+
+## [10.0.0-55](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-54...v10.0.0-55) (2025-10-20)
+
+
+### Features
+
+* display task count in inbox ([#1025](https://github.com/b0ink/doubtfire-deploy/issues/1025)) ([3858e37](https://github.com/b0ink/doubtfire-deploy/commit/3858e3792fef3f0542d954e213987484ac97a990))
+
+
+### Bug Fixes
+
+* display correct marking session details ([c49ffed](https://github.com/b0ink/doubtfire-deploy/commit/c49ffeddedb2b79c39252770692e7df9dcd97bfc))
+* typo ([8a1dd1a](https://github.com/b0ink/doubtfire-deploy/commit/8a1dd1ac806c5acbbf8d1ccf7bdcb6af1c68dd14))
+
+## [10.0.0-54](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-53...v10.0.0-54) (2025-10-17)
+
+## [10.0.0-53](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-52...v10.0.0-53) (2025-10-17)
+
+## [10.0.0-52](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-51...v10.0.0-52) (2025-10-17)
+
+
+### Features
+
+* campus timezone ([#1022](https://github.com/b0ink/doubtfire-deploy/issues/1022)) ([4384b37](https://github.com/b0ink/doubtfire-deploy/commit/4384b376eaedf8ab66febec4c0ada53111311ff0))
+* download marking sessions for tutor ([e3fbe44](https://github.com/b0ink/doubtfire-deploy/commit/e3fbe4404661eb16b9c0e5ba19a1369dac1fb5b0))
+
+
+### Bug Fixes
+
+* display tutor name ([8956020](https://github.com/b0ink/doubtfire-deploy/commit/89560205d7316c53d77f376b5f7cfff5bf99e9c6))
+* filter out students from unit staff editor ([#1018](https://github.com/b0ink/doubtfire-deploy/issues/1018)) ([5c0c997](https://github.com/b0ink/doubtfire-deploy/commit/5c0c9974569e720a6c95aa8333c2a38d0ab4a1b0))
+* re-enable user filtering on click ([697daf2](https://github.com/b0ink/doubtfire-deploy/commit/697daf26693f20f61dcfbbeb0597a02715c9512d))
+
+## [10.0.0-51](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-50...v10.0.0-51) (2025-10-14)
+
+
+### Bug Fixes
+
+* unlock tasks when prerequisite requires rff and task is in aip state ([173d8c2](https://github.com/b0ink/doubtfire-deploy/commit/173d8c25375e231b25e64a04c1e3d2aedf741300))
+
+## [10.0.0-50](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-49...v10.0.0-50) (2025-10-13)
+
+
+### Features
+
+* jplag base code ([#1010](https://github.com/b0ink/doubtfire-deploy/issues/1010)) ([9a7a860](https://github.com/b0ink/doubtfire-deploy/commit/9a7a860fe799e351831e2a3f9701792792b76b38))
+
+## [10.0.0-49](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-48...v10.0.0-49) (2025-10-12)
+
+
+### Features
+
+* display session start and end time ([bb548b9](https://github.com/b0ink/doubtfire-deploy/commit/bb548b92d04bb7b8665444e7935317bc4b6a0981))
+
+## [10.0.0-48](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-47...v10.0.0-48) (2025-10-12)
+
+
+### Features
+
+* add tutor times summary download ([#1015](https://github.com/b0ink/doubtfire-deploy/issues/1015)) ([2b4afe2](https://github.com/b0ink/doubtfire-deploy/commit/2b4afe24e3f8139fec23337facc7656eec9a9d3a))
+
+
+### Bug Fixes
+
+* add assess in portfolio tasks as completed tasks in burndown chart ([8d36c94](https://github.com/b0ink/doubtfire-deploy/commit/8d36c94f03563a0924cfeb7d2d9e8dd911b65960))
+
+## [10.0.0-47](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-46...v10.0.0-47) (2025-09-25)
+
+
+### Features
+
+* add observer only ui ([bfbcee9](https://github.com/b0ink/doubtfire-deploy/commit/bfbcee98cfa4888241c5956372d004ec1cd813cb))
+
+## [10.0.0-46](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-45...v10.0.0-46) (2025-09-24)
+
+## [10.0.0-45](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-44...v10.0.0-45) (2025-09-22)
+
+
+### Bug Fixes
+
+* check for valid unit ([bfae1ad](https://github.com/b0ink/doubtfire-deploy/commit/bfae1ad69bb767cdad47dd8fac35cc84e5134534))
+
+## [10.0.0-44](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-43...v10.0.0-44) (2025-09-18)
+
+## [10.0.0-43](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-42...v10.0.0-43) (2025-09-17)
+
+
+### Bug Fixes
+
+* remove /api from host url ([5889edc](https://github.com/b0ink/doubtfire-deploy/commit/5889edc35bb427dfd4defefa7b77e8c81cd6dd7c))
+
+## [10.0.0-42](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-41...v10.0.0-42) (2025-09-17)
+
+
+### Features
+
+* display result of student imports ([e0503f4](https://github.com/b0ink/doubtfire-deploy/commit/e0503f424dfbef49e68b4e2f697e8d135120634e))
+* enable unit linking to lms context using deeplink ([73de1f1](https://github.com/b0ink/doubtfire-deploy/commit/73de1f1c68223c863c35198b773f0bdeaa1f549e))
+* init lti integration ([248be93](https://github.com/b0ink/doubtfire-deploy/commit/248be93e7c8b5ff060957e500b6a4943882f456c))
+* sync enrolments for all lti members ([5ec499f](https://github.com/b0ink/doubtfire-deploy/commit/5ec499f37b93f2b4bd56d145501fd55ab6af45e6))
+* sync grades for all members ([0f5daa8](https://github.com/b0ink/doubtfire-deploy/commit/0f5daa852f4c3750367f7d2c3f9b0c07ffbfed3f))
+* unauthorised lti banner ([12e65d8](https://github.com/b0ink/doubtfire-deploy/commit/12e65d8f1609f2030b25347beb1c0e06f513f65c))
+
+
+### Bug Fixes
+
+* forward correct lti token ([e95544e](https://github.com/b0ink/doubtfire-deploy/commit/e95544e2e158affefafd2fc0fe9657bf47c200db))
+
+## [10.0.0-41](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-40...v10.0.0-41) (2025-09-15)
+
+## [10.0.0-40](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-39...v10.0.0-40) (2025-09-15)
+
+
+### Features
+
+* assess in portfolio status ([ada4a21](https://github.com/b0ink/doubtfire-deploy/commit/ada4a21c945018abeb9ca1169e48fdd822a27322))
+* ensure complete button updates aip only tasks to working on it ([c8dd020](https://github.com/b0ink/doubtfire-deploy/commit/c8dd020a5e572765f7710c266ae2b20563ab05c2))
+* send automated message when set back to working on it ([a858d3a](https://github.com/b0ink/doubtfire-deploy/commit/a858d3a7b2f41ac7478d4917d2d2f6c68a167e6e))
+* submission type modal ([52f7d40](https://github.com/b0ink/doubtfire-deploy/commit/52f7d404ef154ff27c0ba887c8321360df95ddb6))
+
+
+### Bug Fixes
+
+* typo ([2a2e4a4](https://github.com/b0ink/doubtfire-deploy/commit/2a2e4a41e6ea20152632a9f82ca1723d9a6b56cc))
+* wording ([6291bd6](https://github.com/b0ink/doubtfire-deploy/commit/6291bd61ccc4ff45c708300d0a5014a59a565414))
+
+## [10.0.0-39](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-38...v10.0.0-39) (2025-09-10)
+
+
+### Features
+
+* display warning above task sheet for incomplete prerequisites ([86433bb](https://github.com/b0ink/doubtfire-deploy/commit/86433bb18e6ff9b024d7c397b8744618721b602f))
+
+## [10.0.0-38](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-37...v10.0.0-38) (2025-09-09)
+
+
+### Bug Fixes
+
+* ensure task prerequisites are correctly cached ([3945f4e](https://github.com/b0ink/doubtfire-deploy/commit/3945f4e2ecff1a1d79662c099baf745bfd6f4b7d))
+
+## [10.0.0-37](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-36...v10.0.0-37) (2025-09-09)
+
+## [10.0.0-36](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-35...v10.0.0-36) (2025-09-09)
+
+## [10.0.0-35](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-34...v10.0.0-35) (2025-09-09)
+
+
+### Features
+
+* add task prerequisites ([d5c1a51](https://github.com/b0ink/doubtfire-deploy/commit/d5c1a51dc049b025ae85b2ef6513630a197eabee))
+* cross out prerequisite tasks that have been submitted ([7e1a7db](https://github.com/b0ink/doubtfire-deploy/commit/7e1a7dbd3351dbcc8230f115554d6afdfaca0f70))
+* show list of prerequisites in task dashboard ([111f60a](https://github.com/b0ink/doubtfire-deploy/commit/111f60a4272abcf683c50b711bd43a1f772153ec))
+
+
+### Bug Fixes
+
+* typo ([e0c867f](https://github.com/b0ink/doubtfire-deploy/commit/e0c867fc7f24301d414ed584b6859e09d95c652c))
+
+## [10.0.0-34](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-33...v10.0.0-34) (2025-09-08)
+
+
+### Features
+
+* task assessment counts csv ([c2c6002](https://github.com/b0ink/doubtfire-deploy/commit/c2c6002cef6e5a75f722c964bf2843cf662380f4))
+
+
+### Bug Fixes
+
+* allow scrolling in edit profile form dialog ([ee2b908](https://github.com/b0ink/doubtfire-deploy/commit/ee2b9085a931df551408cd12dbc8187c04d088fc))
+
+## [10.0.0-33](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-32...v10.0.0-33) (2025-09-04)
+
+## [10.0.0-32](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-31...v10.0.0-32) (2025-09-02)
+
+## [10.0.0-31](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-30...v10.0.0-31) (2025-08-27)
+
+
+### Bug Fixes
+
+* use new checked_in comment type ([3033c6c](https://github.com/b0ink/doubtfire-deploy/commit/3033c6ca3c27ad4fc0a7ce4bda3ac44df09516e3))
+
+## [10.0.0-30](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-29...v10.0.0-30) (2025-08-27)
+
+## [10.0.0-29](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-28...v10.0.0-29) (2025-08-27)
+
+## [10.0.0-28](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-27...v10.0.0-28) (2025-08-27)
+
+## [10.0.0-27](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-26...v10.0.0-27) (2025-08-27)
+
+
+### Features
+
+* add option for qr footer text ([9e9b9db](https://github.com/b0ink/doubtfire-deploy/commit/9e9b9dbfb13d7531cb5387060407e23c25212cca))
+* support viewing qr for selected student in tutor view ([1f7fa51](https://github.com/b0ink/doubtfire-deploy/commit/1f7fa51d65767b3186e6eda8970a408cf0e6c847))
+
+
+### Bug Fixes
+
+* add padding when edit profile form displayed as modal ([a3f3b97](https://github.com/b0ink/doubtfire-deploy/commit/a3f3b977251a206b268c369c370934ff69b574d3))
+* show student id ([d1eeefb](https://github.com/b0ink/doubtfire-deploy/commit/d1eeefbea53cb13385bef2b3ba6b04d2f606c391))
+
+## [10.0.0-26](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-25...v10.0.0-26) (2025-08-27)
+
+
+### Features
+
+* add attendance marked task comment ([1559e81](https://github.com/b0ink/doubtfire-deploy/commit/1559e8104c8c4bc137df4f46a0a5ed0e345260af))
+* add tutor discussion to unit dropdown ([90f4149](https://github.com/b0ink/doubtfire-deploy/commit/90f41493313d6a3ad9bdafdd4cd4865c80e94ca2))
+* redirect back to inbox if unit context exists ([b7f4ab9](https://github.com/b0ink/doubtfire-deploy/commit/b7f4ab9e7801fbb33ffc6b4e02bf6613e6d7a524))
+
+
+### Bug Fixes
+
+* enforce minimum height on task comments viewer ([a7588e6](https://github.com/b0ink/doubtfire-deploy/commit/a7588e6f007381c04a570650257cd6f2ff93e5ae))
+* set max width on main container ([d1e5ab1](https://github.com/b0ink/doubtfire-deploy/commit/d1e5ab1427352fcf1b0fdd920cb9838a4f8799a0))
+
+## [10.0.0-25](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-24...v10.0.0-25) (2025-08-26)
+
+
+### Bug Fixes
+
+* only clear draft comment after successfully sending message ([2556355](https://github.com/b0ink/doubtfire-deploy/commit/2556355b0c3e1770fab021ed7d46e462882025b3))
+
+## [10.0.0-24](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-23...v10.0.0-24) (2025-08-21)
+
+## [10.0.0-23](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-22...v10.0.0-23) (2025-08-14)
+
+## [10.0.0-22](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-21...v10.0.0-22) (2025-08-14)
+
+
+### Features
+
+* download tasks awaiting feedback csv ([4a94373](https://github.com/b0ink/doubtfire-deploy/commit/4a943736689e41c405a831ca88a1303fb7c00385))
+
+
+### Bug Fixes
+
+* add return statement ([b8779e7](https://github.com/b0ink/doubtfire-deploy/commit/b8779e70d4decdc837caa8c84e8e8e2dfd3faf7f))
+* show full progress when sidekiq job is complete ([6a60d94](https://github.com/b0ink/doubtfire-deploy/commit/6a60d94b896d173a5008badd1b45bc7f45eee808))
+
+## [10.0.0-21](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-20...v10.0.0-21) (2025-08-12)
+
+
+### Bug Fixes
+
+* clear notification after fetching task comments ([c2d1536](https://github.com/b0ink/doubtfire-deploy/commit/c2d15367d9c53cb426dd12e73dc8d0b779a7f563))
+
+## [10.0.0-20](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-19...v10.0.0-20) (2025-08-11)
+
+## [10.0.0-19](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-18...v10.0.0-19) (2025-08-07)
+
+
+### Features
+
+* mark as discussed button in mobile tutor discussion ([9e62214](https://github.com/b0ink/doubtfire-deploy/commit/9e62214df772355048f4c7c7ffc13f3181f746e2))
+
+## [10.0.0-18](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-17...v10.0.0-18) (2025-07-31)
+
+
+### Bug Fixes
+
+* ensure no progress returns 0 ([9cb1f1a](https://github.com/b0ink/doubtfire-deploy/commit/9cb1f1ab770ac798ec8ecc555ceb73024bc98649))
+
+## [10.0.0-17](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-16...v10.0.0-17) (2025-07-31)
+
+
+### Features
+
+* return task to not started status ([bce1483](https://github.com/b0ink/doubtfire-deploy/commit/bce1483296064bafd44bc538f83731809d816a05))
+
+
+### Bug Fixes
+
+* check if project task stats exist ([e59af80](https://github.com/b0ink/doubtfire-deploy/commit/e59af803f8440338b0210b6a437537372f5dac5b))
+* ensure new comments are fetched for selected project only ([3b0e387](https://github.com/b0ink/doubtfire-deploy/commit/3b0e3877e05f162143fc78065cee9a191d0755f4))
+* ensure whitespace is trimmed ([fe687df](https://github.com/b0ink/doubtfire-deploy/commit/fe687df8b035ef851b9d147e01f575187f428a8c))
+* prevent additional inbox queries ([0d35f7f](https://github.com/b0ink/doubtfire-deploy/commit/0d35f7f4ef2e87dd078edf20e509c8f6efc5c216))
+
+## [10.0.0-16](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-15...v10.0.0-16) (2025-07-29)
+
+## [10.0.0-15](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-14...v10.0.0-15) (2025-07-29)
+
+
+### Bug Fixes
+
+* correctly load start page ([e297f26](https://github.com/b0ink/doubtfire-deploy/commit/e297f261e3df19faf11be9fde6515ce75434c320))
+* map correct variables ([b180799](https://github.com/b0ink/doubtfire-deploy/commit/b180799efd716d802cf83ce08b6df0639d9055b6))
+* prevent report button from expanding accordion ([e715415](https://github.com/b0ink/doubtfire-deploy/commit/e7154157ff1b88817141bae5b1b3de9fd37171f0))
+
+## [10.0.0-14](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-13...v10.0.0-14) (2025-07-28)
+
+
+### Features
+
+* portfolio zip downloading via sidekiq ([9000830](https://github.com/b0ink/doubtfire-deploy/commit/9000830e5e98bc0b921d618c034471eeddf996b4))
+* show list of active sidekiq jobs ([aa85665](https://github.com/b0ink/doubtfire-deploy/commit/aa85665670a23fea556c6420f139e7e7853d16ba))
+* show maintenance ui when api fails ([29bb95f](https://github.com/b0ink/doubtfire-deploy/commit/29bb95f5974dc8f258b204957a212a8399ad2d6e))
+* sidekiq progress modal component ([b9691e7](https://github.com/b0ink/doubtfire-deploy/commit/b9691e793204e84fae323610a7b555b15e065514))
+
+
+### Bug Fixes
+
+* fallback to task definition due date ([bef0a9c](https://github.com/b0ink/doubtfire-deploy/commit/bef0a9cbfefc58407f851d3ca73b90af6b81151f))
+
+## [10.0.0-13](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-12...v10.0.0-13) (2025-07-16)
+
+
+### Bug Fixes
+
+* display task-date-slider only when task exists ([c886034](https://github.com/macite/doubtfire-deploy/commit/c886034d4bf69851f4b06599a45bd1b135c3fff2))
+
+## [10.0.0-12](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-11...v10.0.0-12) (2025-07-15)
+
+
+### Bug Fixes
+
+* exclude jplag route from ngsw cache ([69b55bb](https://github.com/macite/doubtfire-deploy/commit/69b55bb85d8583b6935e08c91aad63e98e6ce887))
+
+## [10.0.0-11](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-10...v10.0.0-11) (2025-07-15)
+
+## [10.0.0-10](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-9...v10.0.0-10) (2025-07-15)
+
+## [10.0.0-9](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-8...v10.0.0-9) (2025-07-15)
+
+## [10.0.0-8](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-7...v10.0.0-8) (2025-07-15)
+
+## [10.0.0-7](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-6...v10.0.0-7) (2025-07-14)
+
+
+### Features
+
+* add icon to discussed in class update ([c11cec0](https://github.com/macite/doubtfire-deploy/commit/c11cec02ece3b6bf7c2e36998ee2fa2a3572bc58))
+* add jplag report download button to student submission tutor teacher footer view ([2ec1b06](https://github.com/macite/doubtfire-deploy/commit/2ec1b06b5330c5e142cc3fc01c49c2aded5411cb))
+* add tutor discussion qr scanning ([e549c4e](https://github.com/macite/doubtfire-deploy/commit/e549c4e0ac9377bd864c81122cffce004681c845))
+* auto send dicussed in class comment ([fbf90c7](https://github.com/macite/doubtfire-deploy/commit/fbf90c7fdfa440f565e2c8abae4ae62bc1fe70eb))
+* **comments:** enhanced drafting for messages using localStorage ([f331734](https://github.com/macite/doubtfire-deploy/commit/f33173409f0804da4de24e1e6ec53971896b28e9))
+* **comments:** enhanced drafting for messages using localStorage ([b6f6831](https://github.com/macite/doubtfire-deploy/commit/b6f683175db6ab9549b8b4481ab54bb89700016a))
+* edit staff note ([b2d8ba4](https://github.com/macite/doubtfire-deploy/commit/b2d8ba41b1dc467a03728d5df4ede6ac6fb3c9a1))
+* fetch new comments on task switching and status updates ([1b7da69](https://github.com/macite/doubtfire-deploy/commit/1b7da692423acaf53b96229b665b971360bacc89))
+* force scanner to use back camera, enhance ux ([e5799b1](https://github.com/macite/doubtfire-deploy/commit/e5799b18ec0603123b6a99b3733919d2611b4fbd))
+* highlight selected task ([25b361f](https://github.com/macite/doubtfire-deploy/commit/25b361f699ad54e45c32167ccae230635a6b2758))
+* indicate reply has been deleted ([c3e288a](https://github.com/macite/doubtfire-deploy/commit/c3e288ae95e2fa5fd43bdf3922e81501f26e1baf))
+* init qr tutor marking state ([41d1d61](https://github.com/macite/doubtfire-deploy/commit/41d1d61a417573e33c4344898ae6d7562c459004))
+* init reply to button ([100a533](https://github.com/macite/doubtfire-deploy/commit/100a5335aa7f37f4477b9d9b8f43806ca32c640b))
+* init staff notes ([c7eed99](https://github.com/macite/doubtfire-deploy/commit/c7eed9903bcc4463f4d0b52889e0d3d9045b03ba))
+* jplag report download button in task explorer view ([16670a2](https://github.com/macite/doubtfire-deploy/commit/16670a27d5dc544ae828da85cda48358cab4d75e))
+* link note that is being replied to ([27726b3](https://github.com/macite/doubtfire-deploy/commit/27726b3f1368031c24bf069bde4f930dd02c1e80))
+* load jplag report viewer into iframe ([1360438](https://github.com/macite/doubtfire-deploy/commit/13604383b403dbf82b013786127a72fab08ac895))
+* localized date time pipe ([5cf752b](https://github.com/macite/doubtfire-deploy/commit/5cf752baf1b9de6105ea2a951ec75818ce1e0cfc))
+* mark task as discussed ([a54f94f](https://github.com/macite/doubtfire-deploy/commit/a54f94f045b4280d0f13304a6b5728b62fc4107e))
+* message tooltip timestamps ([2d79007](https://github.com/macite/doubtfire-deploy/commit/2d79007c033d56218326f55dd8d6821e17fec74f))
+* native jplag report viewer state ([8f6bc6a](https://github.com/macite/doubtfire-deploy/commit/8f6bc6ae5b80502b5d81b65bab2be3aaa56ce42f))
+* qr code generation for student projects ([6f8b94f](https://github.com/macite/doubtfire-deploy/commit/6f8b94f7c0b48d292dfe28871ff7ae7fda86b2b9))
+* redirect back to home if exiting the scanner ([71f09ae](https://github.com/macite/doubtfire-deploy/commit/71f09ae77da6f0a2f5ed135071c92c110940c2f5))
+* reply to ui ([40078df](https://github.com/macite/doubtfire-deploy/commit/40078df477bd300c9b8897976d10658fb91a6d04))
+* show number of notes project has ([f3d4c4c](https://github.com/macite/doubtfire-deploy/commit/f3d4c4c00128ca00dec18ea9950c01df25b75b61))
+* staff note deletion ([0ab1ebf](https://github.com/macite/doubtfire-deploy/commit/0ab1ebff3964d8665d9560bf3007a2d30109ee37))
+* switch to staff note tab view ([65f8c71](https://github.com/macite/doubtfire-deploy/commit/65f8c71bd37ef71481bd14f591af263470987a6e))
+* task and unit ilos ui ([1cef438](https://github.com/macite/doubtfire-deploy/commit/1cef438b458efcee4adce01cc81648d32bb97f35))
+* view filtered tasks only button ([a4e422d](https://github.com/macite/doubtfire-deploy/commit/a4e422dcf3dfa329b83cd7e3d521fc3665c9c45f))
+* view staff notes in portfolio assessment ([020ab5b](https://github.com/macite/doubtfire-deploy/commit/020ab5b9a98a401835e41bee7abc3dee70978c07))
+
+
+### Bug Fixes
+
+* allow oversized images to be scrollable ([8ff67d2](https://github.com/macite/doubtfire-deploy/commit/8ff67d2f8e483813bc3af0dc2429b58fe16f8f2b))
+* avoid fetching twice if no comments are cached yet ([d219d49](https://github.com/macite/doubtfire-deploy/commit/d219d49ca6457119084d4c3d2cb39343dece5aa0))
+* decode url from qr code ([31822ab](https://github.com/macite/doubtfire-deploy/commit/31822abaa4c66e2dcbc3ab74c71fe0f6e9b5861d))
+* discussed context type ([d87e620](https://github.com/macite/doubtfire-deploy/commit/d87e620ab54aa75f03d8295f0319798214ad39f0))
+* display load more button only if there are more tasks ([84d5bca](https://github.com/macite/doubtfire-deploy/commit/84d5bcaa4f6329ed34f30a58d88e18fce9c3a76d))
+* enable unit name text wrapping ([5f779b8](https://github.com/macite/doubtfire-deploy/commit/5f779b8cb626ada2e8b6488812424813cc5e9913))
+* ensure jplag iframe extends to full height ([7ae6466](https://github.com/macite/doubtfire-deploy/commit/7ae6466e6586ceaf58edec8a741c9e2f8a737c0c))
+* ensure scanner doesn't break on ios ([a0b010e](https://github.com/macite/doubtfire-deploy/commit/a0b010ef31161b6cf2ab1038deb82c199ffb9ecb))
+* improve task similarity notice ui ([ba26841](https://github.com/macite/doubtfire-deploy/commit/ba2684192c2a8a139237affc4c0702af901f1808))
+* match comments-modals's original max width ([7edb445](https://github.com/macite/doubtfire-deploy/commit/7edb445d7bc5f61daba1b47a3a36b633b551267e))
+* merge conflict ([e599fe3](https://github.com/macite/doubtfire-deploy/commit/e599fe35ec338cfd7f5ffc419a3379d3f3160312))
+* show edit icon for authors only ([a83a875](https://github.com/macite/doubtfire-deploy/commit/a83a875f25efbe9297fe76d773ef4ff55456bcf8))
+* support markdown ([3ac10f6](https://github.com/macite/doubtfire-deploy/commit/3ac10f6cab39dab42ce19f61029488d220786703))
+* **task-comment-composer:** deleting the key when text is empty ([6ef91e0](https://github.com/macite/doubtfire-deploy/commit/6ef91e0ddb119e03dbaf2b6e53e450e5bcc58b8b))
+* update discussion route ([b5c35d9](https://github.com/macite/doubtfire-deploy/commit/b5c35d9cb0be11a6c6cbd49d9e6aee37bb5b83cd))
+* welcome page redirect issues ([14ad26e](https://github.com/macite/doubtfire-deploy/commit/14ad26e8ed4acf6f77043447a2691360fd62ce51))
+
+## [10.0.0-6](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-5...v10.0.0-6) (2025-06-13)
+
+
+### Features
+
+* add spec con and flexible dates ([a874cff](https://github.com/macite/doubtfire-deploy/commit/a874cff846e788a1820da7e19899ecb20a113888))
+
+
+### Bug Fixes
+
+* only get task feedback templates if staff ([8341b93](https://github.com/macite/doubtfire-deploy/commit/8341b933487ccd451a15cb8f7ec6afc1695cb121))
+* remove unnecessary safe pipe ([c92242a](https://github.com/macite/doubtfire-deploy/commit/c92242ad9afb0a934d4e8fb00afee0c69782c625))
+
+## [10.0.0-5](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-4...v10.0.0-5) (2025-05-30)
+
+
+### Bug Fixes
+
+* correct broken transition hooks ([d237eda](https://github.com/macite/doubtfire-deploy/commit/d237eda82dd989fc1b69d6e4236932050bc5b1a4))
+* delay authorisation code until after authentication ([34410d4](https://github.com/macite/doubtfire-deploy/commit/34410d4afdf5bd73d9c244a6f40c7fb78b714e63))
+* ensure transition in student pages update pdf view ([68a2f38](https://github.com/macite/doubtfire-deploy/commit/68a2f38167c792974aafd907f04fcf9e8b5bf091))
+* simplify setting of auto redirect on login ([60dbceb](https://github.com/macite/doubtfire-deploy/commit/60dbceb2a104894b4eff157c038446796bf01708))
+
+## [10.0.0-4](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-3...v10.0.0-4) (2025-04-15)
+
+
+### Bug Fixes
+
+* ensure sign in waits for global load ([fb6e61f](https://github.com/macite/doubtfire-deploy/commit/fb6e61f874797cd48e77f2a8d0a32f345f5b785b))
+
+## [10.0.0-3](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-2...v10.0.0-3) (2025-04-10)
+
+
+### Bug Fixes
+
+* correct auth callback ([2e087d2](https://github.com/macite/doubtfire-deploy/commit/2e087d267c8bfde633550823aed460379a643ad9))
+
+## [10.0.0-2](https://github.com/macite/doubtfire-deploy/compare/v10.0.0-1...v10.0.0-2) (2025-04-10)
+
+
+### Bug Fixes
+
+* correct handling of errors on auth ([622768a](https://github.com/macite/doubtfire-deploy/commit/622768a06112910d5ba79061689fc0ba486982a5))
+
+## [10.0.0-1](https://github.com/macite/doubtfire-deploy/compare/v8.0.37...v10.0.0-1) (2025-04-10)
+
+
+### Features
+
+* add comment text to comment area ([5c45415](https://github.com/macite/doubtfire-deploy/commit/5c45415eafb38e221963781e4575480294d74e09))
+* add connected learning outcomes ([9532d9f](https://github.com/macite/doubtfire-deploy/commit/9532d9ffe620d67d7bc83137d59e0a91ecf8a73a))
+* add editor for task feedback templates ([3da7a4b](https://github.com/macite/doubtfire-deploy/commit/3da7a4b54a5892ea99cd365ae439677b0f52ed6f))
+* add feedback template picker to comment composer ([9acc365](https://github.com/macite/doubtfire-deploy/commit/9acc36502dc3fd2365817f73ccc742dd3947e293))
+* add field to connect outcomes ([db2d2bc](https://github.com/macite/doubtfire-deploy/commit/db2d2bc32b19165bac8ff6ba0282a16adffbb815))
+* add glo editor ([4efe876](https://github.com/macite/doubtfire-deploy/commit/4efe87694a426c4b6a55ef943576fe47498166e5))
+* add greeting and summary chip ([ed68317](https://github.com/macite/doubtfire-deploy/commit/ed683174ab7b330a48b2aeacdd65012396357479))
+* add option for institution logo ([5141529](https://github.com/macite/doubtfire-deploy/commit/5141529b1ed37f8ca254e89e1b91edac3139f166))
+* add startpage property to auto scroll to on pdf render ([90830cd](https://github.com/macite/doubtfire-deploy/commit/90830cdba73bcfe564362015b5abaf2c40810f5f))
+* combine outcome and template editor ([c12536b](https://github.com/macite/doubtfire-deploy/commit/c12536bc4375b1ae5f19d4e54d0783b6948921eb))
+* edit feedback chips ([04f8dce](https://github.com/macite/doubtfire-deploy/commit/04f8dce30f79d86637abd6db2aa9fa3aa14d1de2))
+* **feedback-template:** add hierarchical sorting for feedback templates ([8db1844](https://github.com/macite/doubtfire-deploy/commit/8db1844402d080902a9d3938ecc04dd7a2f0340f))
+* **feedback-template:** improve sorting logic for feedback templates ([d3f156a](https://github.com/macite/doubtfire-deploy/commit/d3f156a640e6a3da99c147c4c2303a1af3050f8f))
+* **feedback-template:** merge upstream changes ([0fb1d8a](https://github.com/macite/doubtfire-deploy/commit/0fb1d8abfa3264befddd97e1ce0832359866919d))
+* fetch feedback chips when selecting task in unit editor ([9caeda4](https://github.com/macite/doubtfire-deploy/commit/9caeda4e71946ddb7907027ebf7a54397f11606f))
+* init pdf viewer on page 2 for student submissions ([45cc89e](https://github.com/macite/doubtfire-deploy/commit/45cc89e18cff8fcde66480448eea178940d316a2))
+* make template editor reusable across contexts ([a42f177](https://github.com/macite/doubtfire-deploy/commit/a42f177f61b3f7166edf3b9c93dfd2dcd3949df7))
+* modify fields and operate on learning outcomes ([65ffc4f](https://github.com/macite/doubtfire-deploy/commit/65ffc4f8d61a9d0b2cc7c5f8d13db932436fc4f5))
+* persistent pdf zoom level ([1348da2](https://github.com/macite/doubtfire-deploy/commit/1348da2d345b080002a62a24d717fac15c4b0a92))
+* rebuild auth with refresh token ([5d3a36e](https://github.com/macite/doubtfire-deploy/commit/5d3a36e8f16e8993c5607241112c490361a31c97))
+* show chips on picker ([e54216f](https://github.com/macite/doubtfire-deploy/commit/e54216f8472b67cf9f2ff69c9bcb3355efa90081))
+* suggest task status according to feedback ([95796da](https://github.com/macite/doubtfire-deploy/commit/95796da4e7855bcd76023754d95cec8d033668fc))
+
+
+### Bug Fixes
+
+* adjust ui for nested csv modal ([d7fac6f](https://github.com/macite/doubtfire-deploy/commit/d7fac6f0fb23f060373fd4c40f5e82b960098c77))
+* check difference before updating linked outcomes ([aa0bc76](https://github.com/macite/doubtfire-deploy/commit/aa0bc76d32656dc14d28ebca520e4f90d6d9434e))
+* check for unit context for template csv name ([6ecec84](https://github.com/macite/doubtfire-deploy/commit/6ecec8483222304161f9cda87a8bdf3ecd774bcb))
+* comment console logs ([e86162d](https://github.com/macite/doubtfire-deploy/commit/e86162d1592d8418bab3c889b1d0bbfe3662b86d))
+* correct a typo in /edit_profile ([4b7901f](https://github.com/macite/doubtfire-deploy/commit/4b7901fd03d9652230aecb52791313f052d7b53d))
+* correct string manipulation for task period formatting ([aa06cbf](https://github.com/macite/doubtfire-deploy/commit/aa06cbf3a08efa02f508062d47c127289fc8c6fa))
+* display feedback picker chips in color ([6ae0771](https://github.com/macite/doubtfire-deploy/commit/6ae077149d16e5af538d432a9ce2db974298b549))
+* display nested feedback groups in picker ([db93b18](https://github.com/macite/doubtfire-deploy/commit/db93b187cfe6e197a3b6243445809ec6d767d4bd))
+* display task status in coloured chip in feedback editor ([73986e8](https://github.com/macite/doubtfire-deploy/commit/73986e89839603b19f643404e5a48964ac9a3c1b))
+* exclude greeting and summary from selected ([1978e54](https://github.com/macite/doubtfire-deploy/commit/1978e5410f6aa9314252ae3cf922917033f551f0))
+* feedback picker search ([3a5e02e](https://github.com/macite/doubtfire-deploy/commit/3a5e02e2f7864b9e1ea4f8d49ce69d23addbcc5a))
+* **feedback-template-editor:** add missing operator ([a1f220c](https://github.com/macite/doubtfire-deploy/commit/a1f220c9f5462a2b91035eb936f3ff25b7d4cd67))
+* **feedback-template-editor:** improve feedback template sorting logic ([bd48c40](https://github.com/macite/doubtfire-deploy/commit/bd48c40c4c3b0a3d67d41373b624ed8b3b754f4a))
+* **feedback-template:** ensure sorting defaults are applied ([96f7555](https://github.com/macite/doubtfire-deploy/commit/96f7555f9dfcf4c62ee3dd54b9e9e74087bec47a))
+* focus on composer input after selecting chip ([95afdff](https://github.com/macite/doubtfire-deploy/commit/95afdff12aac914bb8af8727f8b8d960bc3b072c))
+* hide feedback group comment text ([55a6625](https://github.com/macite/doubtfire-deploy/commit/55a662584c4b8b6123335bcbc5149a9f0f0701d8))
+* hide picker on task change ([198b7f9](https://github.com/macite/doubtfire-deploy/commit/198b7f9d0ccad631059ed5e1ad5487604582074b))
+* hide summary instead of comment text for groups ([7644cba](https://github.com/macite/doubtfire-deploy/commit/7644cba92f92f5c261501c1907f7f5418538e3a7))
+* hide template editor on outcome switch ([a3706e9](https://github.com/macite/doubtfire-deploy/commit/a3706e9394b78edf9ec15030246c98acca9d3852))
+* lock ng2-pdf-viewer to 10.2.2 ([2cf6215](https://github.com/macite/doubtfire-deploy/commit/2cf62152524ec728c73aaa23f678bbce9ae6b4d1))
+* only get feedback chips if staff ([7f4381e](https://github.com/macite/doubtfire-deploy/commit/7f4381e1179400642821068d1acdba346a2969a2))
+* organise outcome and feedback csv functionality ([641f1d9](https://github.com/macite/doubtfire-deploy/commit/641f1d9f75177d48b3911c300e23c901e0a603e1))
+* prevent saving empty items ([0eed547](https://github.com/macite/doubtfire-deploy/commit/0eed5477940db511cee09bbdb7ccb0adde57cbbf))
+* refine feedback editor ([f410cfe](https://github.com/macite/doubtfire-deploy/commit/f410cfe47b0c242f1a164c6345bc4d29f5659f6a))
+* refresh context when changing task defs ([c078bcf](https://github.com/macite/doubtfire-deploy/commit/c078bcf40198c5495d4c94354fab411d3e0ee934))
+* remove duplicate alert ([955d0a1](https://github.com/macite/doubtfire-deploy/commit/955d0a17ae23be77954145ba189ac8bb0aa90da0))
+* remove glo outcome specific csv buttons ([579c491](https://github.com/macite/doubtfire-deploy/commit/579c49170808a6fcca1bfd67f0923f0339255463))
+* remove selected outcomes from dropdown ([5494384](https://github.com/macite/doubtfire-deploy/commit/5494384f1a4becf462dd031e89fae2734ed46e55))
+* reply to position and wrapping ([e065c46](https://github.com/macite/doubtfire-deploy/commit/e065c46c3fe6689a7b295a3f91a576eabc11f7bd))
+* reset template table for new outcome ([7585c1c](https://github.com/macite/doubtfire-deploy/commit/7585c1c60ca8fe168084ec3d857aec4856bfb0db))
+* save connected outcomes ([eb3cf6d](https://github.com/macite/doubtfire-deploy/commit/eb3cf6daf99b893642336efb48ed58bc9c8b8207))
+* save linked outcomes upon selection ([07f103d](https://github.com/macite/doubtfire-deploy/commit/07f103de46defa2a78f5d7c43030e67255c0d5fe))
+* save to proper cache and clean up ([80edff2](https://github.com/macite/doubtfire-deploy/commit/80edff2ef04137b3d38ccdd252788cfb40f458f0))
+* show description for connected outcome selection ([0f2c164](https://github.com/macite/doubtfire-deploy/commit/0f2c164579491298b81849cc110301229f2ca5af))
+* show picker above composer and below comments ([ef524f1](https://github.com/macite/doubtfire-deploy/commit/ef524f1b048b3bfe1781307a34e1a5fe364943a4))
+* show proper selected outcome or template ([0ac3881](https://github.com/macite/doubtfire-deploy/commit/0ac3881e7f6d0f5b7ddeb85e5e8f9e6835c5dba6))
+* support hot reloading on windows ([247330e](https://github.com/macite/doubtfire-deploy/commit/247330e46d74186519dbcbbef8378b508eaca83f))
+* support windows build ([3594528](https://github.com/macite/doubtfire-deploy/commit/35945288c73052afd28209a7381335af2b14ba10))
+* template editor not showing if new ([6cfe379](https://github.com/macite/doubtfire-deploy/commit/6cfe37999fa7f484e1b89f4dbe657b99907adae1))
+* tlo csv url ([73588d8](https://github.com/macite/doubtfire-deploy/commit/73588d886118861bbd3e5ae380cddc7baa532dbb))
+* update packages ([e5a57f8](https://github.com/macite/doubtfire-deploy/commit/e5a57f8ba3669345a272b308b0c892fdab4843ef))
+* upon save update entity in frontend ([a128744](https://github.com/macite/doubtfire-deploy/commit/a1287449b945295ced60e86a21bcaab67ed0f9bd))
+* use icon instead of color for template task status ([11a3993](https://github.com/macite/doubtfire-deploy/commit/11a399324ee64af901f44f28de3526cbd7650bac))
+* use task status color for recommended button ([174cde0](https://github.com/macite/doubtfire-deploy/commit/174cde0022d0c728d2c6408d40129d6c343aa91d))
+
+### [8.0.37](https://github.com/macite/doubtfire-deploy/compare/v8.0.36...v8.0.37) (2025-03-10)
+
+
+### Bug Fixes
+
+* ensure overseer download get resources zip ([a1e4d71](https://github.com/macite/doubtfire-deploy/commit/a1e4d71b34733bc53cd2cfa63ee1e70f8f43386f))
+
+### [8.0.36](https://github.com/macite/doubtfire-deploy/compare/v8.0.35...v8.0.36) (2025-03-07)
+
+
+### Bug Fixes
+
+* overseer image admin in unit and task ([72c89b3](https://github.com/macite/doubtfire-deploy/commit/72c89b30cca426d56c7efbbac34e00fd734fe714))
+
+### [8.0.35](https://github.com/macite/doubtfire-deploy/compare/v8.0.34...v8.0.35) (2025-02-28)
+
+
+### Bug Fixes
+
+* correct ability to edit teaching periods ([ce37bf2](https://github.com/macite/doubtfire-deploy/commit/ce37bf27689744a6f43be46912be0d14e2cdbc6b))
+* ensure an old grade is present in portfolio assessment ([7daacd1](https://github.com/macite/doubtfire-deploy/commit/7daacd1bf0af050aeb6db503d963e574f68b9a94))
+* ensure overseer images can be edited ([3479b7a](https://github.com/macite/doubtfire-deploy/commit/3479b7af589b9ba986ddbb7dc8a84fd0be3e66e6))
+
+### [8.0.34](https://github.com/macite/doubtfire-deploy/compare/v8.0.33...v8.0.34) (2025-02-12)
+
+
+### Bug Fixes
+
+* enhance rollover messages ([48348c8](https://github.com/macite/doubtfire-deploy/commit/48348c8e6a7ffdb4186fe3e6d27fe860076922e1))
+
+### [8.0.33](https://github.com/macite/doubtfire-deploy/compare/v8.0.32...v8.0.33) (2025-01-29)
+
+
+### Bug Fixes
+
+* nginx conf redirect for spa ([95c0ca9](https://github.com/macite/doubtfire-deploy/commit/95c0ca9384e5a0fe7e522f61146b711b706712c1))
+
+### [8.0.32](https://github.com/macite/doubtfire-deploy/compare/v8.0.31...v8.0.32) (2025-01-29)
+
+
+### Bug Fixes
+
+* correct issue fetching user from cookie ([0d63775](https://github.com/macite/doubtfire-deploy/commit/0d63775339dcbb648d7c8702b1c3428b8622a704))
+
+### [8.0.31](https://github.com/macite/doubtfire-deploy/compare/v8.0.30...v8.0.31) (2025-01-28)
+
+
+### Bug Fixes
+
+* ensure test attempt work for staff students ([06bfa71](https://github.com/macite/doubtfire-deploy/commit/06bfa714d0b54938231004a29ae11e68fe515d80))
+* serve index.html for unknown paths ([d07ad26](https://github.com/macite/doubtfire-deploy/commit/d07ad26bd7e7bd8ba7c7877a1208a8f82958c72b))
+
+### [8.0.30](https://github.com/doubtfire-lms/doubtfire-deploy/compare/v8.0.29...v8.0.30) (2025-01-22)
+
+
+### Features
+
+* add ability to collect d2l org and grade object ids ([93fd614](https://github.com/doubtfire-lms/doubtfire-deploy/commit/93fd6144c798fd302204728a6c2dd43f74d7bf91))
+* add ability to trigger d2l grade transfer ([fd0cc07](https://github.com/doubtfire-lms/doubtfire-deploy/commit/fd0cc0737682e9aa02af14f858c8a5f5f27f22e7))
+* add success page for oauth callback ([921406e](https://github.com/doubtfire-lms/doubtfire-deploy/commit/921406ed595c7a74cb750bc87d49fac0337bdb8a))
+* check if d2l results available ([2460c88](https://github.com/doubtfire-lms/doubtfire-deploy/commit/2460c88af08740f8626ed71d8851a75af095a03d))
+* d2l integration checks settings ([0a61df5](https://github.com/doubtfire-lms/doubtfire-deploy/commit/0a61df53c9fc187b7462e8564a19641ac1693cf0))
+* switch to html5 mode ([9d017fb](https://github.com/doubtfire-lms/doubtfire-deploy/commit/9d017fbfaa1d5bae7f43da37ec7ae43e1dfafa8f))
+
+
+### Bug Fixes
+
+* ensure standard error propagate to the ui ([fd056db](https://github.com/doubtfire-lms/doubtfire-deploy/commit/fd056db47631087d5ef29223bd3dc9037c4b10dd))
+* report better errors during file downloads ([cf87d68](https://github.com/doubtfire-lms/doubtfire-deploy/commit/cf87d68ea3a5433a073ae6db413f8c1f825ada80))
+
+### [8.0.29](https://github.com/macite/doubtfire-deploy/compare/v8.0.28...v8.0.29) (2024-10-25)
+
+
+### Features
+
+* add support for the view language ([151ca04](https://github.com/macite/doubtfire-deploy/commit/151ca04ca8da9059890d7846293bf34ae67f05c5))
+
+
+### Bug Fixes
+
+* correct task scroll into view ([7984b39](https://github.com/macite/doubtfire-deploy/commit/7984b393752381e0a219dbbdafc0e7b44c3fcdb3))
+
+### [8.0.27](https://github.com/macite/doubtfire-deploy/compare/v8.0.26...v8.0.27) (2024-08-09)
+
+
+### Bug Fixes
+
+* encode . in the username in scorm player ([1ec1754](https://github.com/macite/doubtfire-deploy/commit/1ec175419ab3ef80f22ac8a6384f6ed40a17c217))
+
+### [8.0.26](https://github.com/macite/doubtfire-deploy/compare/v8.0.25...v8.0.26) (2024-08-09)
+
+
+### Bug Fixes
+
+* register service names in state resolve functions ([64e41f4](https://github.com/macite/doubtfire-deploy/commit/64e41f4aedcf519a2786d7fe4929900f86731896))
+
+### [8.0.25](https://github.com/macite/doubtfire-deploy/compare/v8.0.24...v8.0.25) (2024-08-09)
+
+
+### Bug Fixes
+
+* adjust shortcuts and unregister on inbox destroy ([8a2ce3b](https://github.com/macite/doubtfire-deploy/commit/8a2ce3bfe32d99be1bc0667ca1aba55d36f987d2))
+* switch to control shift ([a0c1185](https://github.com/macite/doubtfire-deploy/commit/a0c1185bf4af3acb8f58c97cc27021c952861603))
+
+### [8.0.24](https://github.com/macite/doubtfire-deploy/compare/v8.0.23...v8.0.24) (2024-08-09)
+
+
+### Features
+
+* add ability to preview scorm test ([7ffbea9](https://github.com/macite/doubtfire-deploy/commit/7ffbea9f56c393d44571c18f30735e090701b554))
+* add new Numbas Feature ([6f1ac4e](https://github.com/macite/doubtfire-deploy/commit/6f1ac4e4c1a78115c426838bce350bce286e44f0))
+* add new Numbas Feature ([8eba913](https://github.com/macite/doubtfire-deploy/commit/8eba913ec4462e1c252a8a698b4b6de67c4ec25f))
+* add numbas component ([7c1734b](https://github.com/macite/doubtfire-deploy/commit/7c1734b137ac369b3b605b38749c845df38b9a78))
+* add Numbas config options to task def service keys ([ff28e48](https://github.com/macite/doubtfire-deploy/commit/ff28e4802b86dc22be339cc196ef82ade67934f7))
+* add Numbas test attempt model ([097df79](https://github.com/macite/doubtfire-deploy/commit/097df7960d34a8c905ee495b8c6c8b8843e43b36))
+* add Numbas test section on ready for feedback ([e61295c](https://github.com/macite/doubtfire-deploy/commit/e61295c50006dbc89cfc0639bb3c68a09a3cda9d))
+* add Numbas test upload section and reorder editor sections ([edbd536](https://github.com/macite/doubtfire-deploy/commit/edbd536e73ace8b6b4cced1481c809fd36524dce))
+* add Numbas upload component and related functions to task-definition model ([4ecaee8](https://github.com/macite/doubtfire-deploy/commit/4ecaee8ad1c0caed9a5d848066a173000989f79b))
+* add test attempt service ([a576f48](https://github.com/macite/doubtfire-deploy/commit/a576f484bc434728eab9632c022bccf1ed26cb01))
+* add test attempt service and minor numbas related changes ([0652b56](https://github.com/macite/doubtfire-deploy/commit/0652b56f69eb850c2dfb43be54d07beb4c4eb469))
+* added numbas-lms service code ([471d344](https://github.com/macite/doubtfire-deploy/commit/471d34486f2582e95aac84469187f087277cc079))
+* allow changing scorm review config and add minor UI changes ([fc023af](https://github.com/macite/doubtfire-deploy/commit/fc023af462656e3557e2970f211fa4d59ee1e3d5))
+* change Numbas time delay config to enable incremental delays ([0afa719](https://github.com/macite/doubtfire-deploy/commit/0afa7197293c90a154c1716db91ecadae3677d53))
+* disable attempt button if passed and add button to review latest attempt in card ([703563c](https://github.com/macite/doubtfire-deploy/commit/703563c86253c60ad30d451ee2d8e0fa7ebbfabb))
+* display numbas task comments ([48a31da](https://github.com/macite/doubtfire-deploy/commit/48a31da1442f1e14f497c614053d9002c9f2631b))
+* enable reviewing, passing, deleting test attempts and add test attempt model and service ([561b924](https://github.com/macite/doubtfire-deploy/commit/561b9241c2f44fd69d3f09c656a025f514bbaf3a))
+* enable students to request extra scorm attempt ([d904ffd](https://github.com/macite/doubtfire-deploy/commit/d904ffd6fd674f6e61894d517f5aa147d1db6d29))
+* get unique token for scorm asset retrieval ([8fe2f9e](https://github.com/macite/doubtfire-deploy/commit/8fe2f9e9ca60c02e55c24d0f2bb3eb6dbea8217c))
+* implement numbas test data upload in task definition service ([2c7dab5](https://github.com/macite/doubtfire-deploy/commit/2c7dab555fc9a226b980a8dae96f5738bf517b1b))
+* insert Numbas test rules options in the task editor ([7e52ad5](https://github.com/macite/doubtfire-deploy/commit/7e52ad5e5759291d7d61805070ec482eb49c90be))
+* numbas-test-numbas-service ([cee13b7](https://github.com/macite/doubtfire-deploy/commit/cee13b727b35f804fc16070885f0e57c422c9982))
+* prevent uploading files until scorm passed ([ec86e4e](https://github.com/macite/doubtfire-deploy/commit/ec86e4eca8e9c50ebdaf54c34c302da88dd44b31))
+* show banner based on scorm success status ([db16172](https://github.com/macite/doubtfire-deploy/commit/db161721fab1359694c44d8df7237cd01892938b))
+* show launch button on ready for feedback if Numbas test is enabled for the task ([17af5b7](https://github.com/macite/doubtfire-deploy/commit/17af5b7fe9e1fbc8e5f18e2fece6e029b017408c))
+* use confirmation modal when passing or deleting test attempts ([3fb25bb](https://github.com/macite/doubtfire-deploy/commit/3fb25bb373acab837a3ef787620dd654bfd6803f))
+
+
+### Bug Fixes
+
+* add accepted Numbas file types ([bcaa8af](https://github.com/macite/doubtfire-deploy/commit/bcaa8af150aaf68b5cf5fb07ad9cb72037212d5d))
+* add auth headers to scorm adapter xhr requests ([97e1ea1](https://github.com/macite/doubtfire-deploy/commit/97e1ea187b769a51ebb66b82d76f21193bb29386))
+* adjusted edit profile accidental change ([316abc7](https://github.com/macite/doubtfire-deploy/commit/316abc775eeba6ca846c7c742bc88bec61d05a5e))
+* change success status descriptions ([20da042](https://github.com/macite/doubtfire-deploy/commit/20da0423450fe3b9b8006660bf6f5e06670f520c))
+* delete comment as well as test attempt ([b6887e8](https://github.com/macite/doubtfire-deploy/commit/b6887e85b7a06b166519924dd682ef57650a4e32))
+* disable launch scorm test button if user is staff ([9bc48b0](https://github.com/macite/doubtfire-deploy/commit/9bc48b03905c0a839d78ea13be9817ca73ba54cf))
+* ensure counters are incremented after object creation ([2b1dcfc](https://github.com/macite/doubtfire-deploy/commit/2b1dcfc717eb770dd623c62ac99d8d961d1c3124))
+* ensure datamodel is updated on termination ([2ac487f](https://github.com/macite/doubtfire-deploy/commit/2ac487f13c3743f2b5b805521964bdcbca7cdc15))
+* ensure scorm frame loads when src is available ([6ae4295](https://github.com/macite/doubtfire-deploy/commit/6ae42954162996acf145cef211981153381ff49d))
+* hide config options if numbas test is disabled ([0b15b1c](https://github.com/macite/doubtfire-deploy/commit/0b15b1c38b8f09a30f53bb42dccd1971109bce01))
+* indicate task def has scorm data when zip is uploaded ([4b983ba](https://github.com/macite/doubtfire-deploy/commit/4b983bae1f522013f750b8c36d5e1a52624abbbc))
+* initialise SCORM API wrapper before iframe loads ([5d0606c](https://github.com/macite/doubtfire-deploy/commit/5d0606c56eaeb929d5873cae7038ce729581512c))
+* integrate Numbas services well with the existing system ([d47b8ed](https://github.com/macite/doubtfire-deploy/commit/d47b8edc745660801984346c4fa67d6bb371f4cb))
+* marking shortcuts no longer conflict with common browser shortcuts ([ec7524a](https://github.com/macite/doubtfire-deploy/commit/ec7524aaae040794de76521363c2d885a0425db0))
+* remove attempt number field ([f0ff40b](https://github.com/macite/doubtfire-deploy/commit/f0ff40bfd7e86b904adae94bf8cbe5d56371e011))
+* remove saved scorm token and always get ([c8fc702](https://github.com/macite/doubtfire-deploy/commit/c8fc702bde6f1c448fc585042e5265625552bae8))
+* retrieve test attempt data correctly ([2810ce6](https://github.com/macite/doubtfire-deploy/commit/2810ce65d423a137600d621a98bf27dbc221921c))
+* send task id with numbas completed attempt data ([e46214d](https://github.com/macite/doubtfire-deploy/commit/e46214dd59d86014bb04d1c31f3a3bf21240e32b))
+* show correct attempts left and allow tutor to review attempt always ([58c24c3](https://github.com/macite/doubtfire-deploy/commit/58c24c3a6760af168a918bc099e755f475eac5f0))
+* show correct Numbas test from the task def with all assets loaded ([bee0a0b](https://github.com/macite/doubtfire-deploy/commit/bee0a0bb1eb905c964caf7888419effe70553520))
+* show delete and download buttons in editor when Numbas test exists ([821feb9](https://github.com/macite/doubtfire-deploy/commit/821feb93a8a40d095ef4f50adedbfd9216278fa9))
+* show Numbas button component and modify iframe request ([c9c2fbe](https://github.com/macite/doubtfire-deploy/commit/c9c2fbe10db156512946355f3cabfe54461f0eba))
+* show Numbas iframe on top of other elements ([f53befd](https://github.com/macite/doubtfire-deploy/commit/f53befd82b52bba24bc8c593a9266c11f9155d32))
+* show previously configured Numbas attempt limit ([56e1a5d](https://github.com/macite/doubtfire-deploy/commit/56e1a5de44ee275f9544b77909b7472f1020c2c3))
+* update numbas api path ([3df59dc](https://github.com/macite/doubtfire-deploy/commit/3df59dcc58f021ef16d81938d1d05473818bc75e))
+* use modal for Numbas and enable authentication ([64b1bfb](https://github.com/macite/doubtfire-deploy/commit/64b1bfb2918993e58a6659948d9621fc7d0b8ba4))
+* use nullish coalescing when retrieving data from the datamodel ([226d919](https://github.com/macite/doubtfire-deploy/commit/226d9193251fb675c92bec8265508477857a4ec9))
+
+### [8.0.28](https://github.com/macite/doubtfire-deploy/compare/v8.0.23...v8.0.28) (2024-09-05)
+
+
+### Features
+
+* add support for the view language ([b4b3654](https://github.com/macite/doubtfire-deploy/commit/b4b36541943232f7df3ee4888bf4ca46c4c04018))
+
+### [8.0.23](https://github.com/macite/doubtfire-deploy/compare/v8.0.22...v8.0.23) (2024-08-02)
+
+
+### Features
+
+* allow pdf download and switch to native viewer ([16ac9c8](https://github.com/macite/doubtfire-deploy/commit/16ac9c8bc488607025ee95dbb4b41134a5346b00))
+
+
+### Bug Fixes
+
+* ensure overseer menu only shows when active for task ([c04e9fe](https://github.com/macite/doubtfire-deploy/commit/c04e9fe3a824e316a632b3f7b53b08887e57c831))
+
+### [8.0.22](https://github.com/macite/doubtfire-deploy/compare/v8.0.21...v8.0.22) (2024-07-26)
+
+
+### Bug Fixes
+
+* correct duplicate shortcut registration ([dca03a4](https://github.com/macite/doubtfire-deploy/commit/dca03a41f502947136e301e51242570169826dd9))
+
+### [8.0.21](https://github.com/macite/doubtfire-deploy/compare/v8.0.20...v8.0.21) (2024-07-24)
+
+
+### Bug Fixes
+
+* ensure groups can be locked ([b671065](https://github.com/macite/doubtfire-deploy/commit/b67106589b71660bdfe01151deb68d3092b9091f))
+
+### [8.0.20](https://github.com/macite/doubtfire-deploy/compare/v8.0.19...v8.0.20) (2024-07-15)
+
+
+### Bug Fixes
+
+* ensure turn it in eula is accessible by all ([3d44c11](https://github.com/macite/doubtfire-deploy/commit/3d44c115b498366b80e5b8b99ccd3eaa660ff613))
+
+### [8.0.19](https://github.com/macite/doubtfire-deploy/compare/v8.0.18...v8.0.19) (2024-07-10)
+
+
+### Features
+
+* add ability to minimise task details in task viewer ([e904640](https://github.com/macite/doubtfire-deploy/commit/e9046400e827481e09492bcb458b00b4e3aca347))
+
+
+### Bug Fixes
+
+* ensure pdf is visible in task viewer mobile/narrow ([f5388e4](https://github.com/macite/doubtfire-deploy/commit/f5388e45715597f0c92f252f721cf274bb8de0d5))
+* task def editor so save is not over fields and header visible for task being edited ([22a2616](https://github.com/macite/doubtfire-deploy/commit/22a26165c9f2ef634b3e79d1e5d5d6b74e6f2731))
+
+### [8.0.18](https://github.com/macite/doubtfire-deploy/compare/v8.0.17...v8.0.18) (2024-07-03)
+
+
+### Features
+
+* add ability to set unit code on import from rollover ([d47d105](https://github.com/macite/doubtfire-deploy/commit/d47d10561bd7bfad495f878ebb0bab399ce144b7))
+
+### [8.0.17](https://github.com/macite/doubtfire-deploy/compare/v8.0.16...v8.0.17) (2024-07-01)
+
+
+### Bug Fixes
+
+* correct broken template in activities list ([4d81fa1](https://github.com/macite/doubtfire-deploy/commit/4d81fa1fabb07e40d0d42068f23b8e756f4cdf4b))
+* correct campus list templates ([1795895](https://github.com/macite/doubtfire-deploy/commit/17958955f65bab2b268826f2a7e19f311071615b))
+* switch date formats to date-fns ([2394efb](https://github.com/macite/doubtfire-deploy/commit/2394efb07c03da18fb8114d87b0c89e68bf7fd2d))
+
+### [8.0.16](https://github.com/macite/doubtfire-deploy/compare/v8.0.15...v8.0.16) (2024-06-28)
+
+
+### Bug Fixes
+
+* ensure drop works on task resource upload in windows ([f0505d7](https://github.com/macite/doubtfire-deploy/commit/f0505d7b62b97f1b821b19b1c4178973503789da))
+
+### [8.0.15](https://github.com/macite/doubtfire-deploy/compare/v8.0.14...v8.0.15) (2024-06-28)
+
+
+### Bug Fixes
+
+* ensure zip uploads work on windows ([1857bdc](https://github.com/macite/doubtfire-deploy/commit/1857bdc708c81dc39d467f42c9e316a8664dc080))
+
+### [8.0.14](https://github.com/macite/doubtfire-deploy/compare/v8.0.13...v8.0.14) (2024-06-27)
+
+
+### Bug Fixes
+
+* ensure unit import allows unit code change ([01013d3](https://github.com/macite/doubtfire-deploy/commit/01013d3d16ab5b704acc589d34975693030def0b))
+* ensure unit load sets main convenor user in all cases ([4c96624](https://github.com/macite/doubtfire-deploy/commit/4c966245d270af46107aae94bdd38e84b2188b65))
+
+### [8.0.13](https://github.com/macite/doubtfire-deploy/compare/v8.0.12...v8.0.13) (2024-06-27)
+
+
+### Bug Fixes
+
+* update discuss text to promote use ([7b5d35c](https://github.com/macite/doubtfire-deploy/commit/7b5d35c89a577d0408a51d8390400f44615591eb))
+
+### [8.0.12](https://github.com/macite/doubtfire-deploy/compare/v8.0.11...v8.0.12) (2024-06-25)
+
+
+### Bug Fixes
+
+* ensure null fields will not break in entity mapping ([32046e8](https://github.com/macite/doubtfire-deploy/commit/32046e89eaaf2a9b36e88f078a7b695b1506886d))
+* reinstate unit import for teaching period ([991230f](https://github.com/macite/doubtfire-deploy/commit/991230f7b527957116263b6abaa588ed43d3a787))
+
+### [8.0.11](https://github.com/macite/doubtfire-deploy/compare/v8.0.10...v8.0.11) (2024-06-24)
+
+
+### Bug Fixes
+
+* ensure turn it in only appears to task editor when enabled ([3a81688](https://github.com/macite/doubtfire-deploy/commit/3a81688904159c8c9e3efdde8f3605aa5677630f))
+* task date picker allows direct entry ([2f91e8f](https://github.com/macite/doubtfire-deploy/commit/2f91e8fbba7902e1e22871cc82c87f4ff797b058))
+
+### [7.0.24](https://github.com/macite/doubtfire-deploy/compare/v7.0.23...v7.0.24) (2024-06-05)
+
+
+### Bug Fixes
+
+* ensure download blob supports 206 responses ([6445f9f](https://github.com/macite/doubtfire-deploy/commit/6445f9f998db70fbe9b9abf723dc17fd298b5d2f))
+
+### [8.0.10](https://github.com/macite/doubtfire-deploy/compare/v7.0.23...v8.0.10) (2024-06-21)
+
+
+### Bug Fixes
+
+* ensure loading screen removed in sign in component ([729c438](https://github.com/macite/doubtfire-deploy/commit/729c438f7f6a988f5ff2fa4035493b5cae1c98f7))
+* ensure portfolio can get grades from strings ([fd916b9](https://github.com/macite/doubtfire-deploy/commit/fd916b94e9012c0909c5880f377f93d326c18e4d))
+* ensure portfolio only shown when it exists ([ce970f5](https://github.com/macite/doubtfire-deploy/commit/ce970f5fdc4f42708edc71c98dc9d869056e9e2c))
+* ensure tii open report alerts errors ([c5dd45c](https://github.com/macite/doubtfire-deploy/commit/c5dd45c57fd34e8373f11d065a0cd08edef0a794))
+* fix pdf viewer for portfolios ([da376b9](https://github.com/macite/doubtfire-deploy/commit/da376b9bc7905aadeb4d234cbb4f595e4c4424a1))
+* grade icon for portfolio page ([aab1b9c](https://github.com/macite/doubtfire-deploy/commit/aab1b9c3e7365cdf7dd64d767fdb6e5a9a2436a3))
+* show burndown and pie chart on portfolio page ([e70f4c7](https://github.com/macite/doubtfire-deploy/commit/e70f4c7cd1395eaab942ee389788f75f92e985c9))
+
+### [8.0.9](https://github.com/macite/doubtfire-deploy/compare/v7.0.22...v8.0.9) (2024-05-31)
+
+
+### Bug Fixes
+
+* comment out fix and complete hotkeys temporarily ([c1f623d](https://github.com/macite/doubtfire-deploy/commit/c1f623d17432f3563366ef32fc3da3b54dbef0b7))
+
+### [8.0.8](https://github.com/macite/doubtfire-deploy/compare/v8.0.7...v8.0.8) (2024-05-30)
+
+
+### Features
+
+* added percentage to tooltip when hovering student's task progress ([69ed613](https://github.com/macite/doubtfire-deploy/commit/69ed61380bd7681d4496cb109e628111873b6e82))
+
+
+### Bug Fixes
+
+* change from hiding element to not loading ([414da65](https://github.com/macite/doubtfire-deploy/commit/414da65fcef9b10367f53112239a60e1b271d475))
+* change grade values, change grade display ([081c35b](https://github.com/macite/doubtfire-deploy/commit/081c35ba0fbc7ab4ddb2fba3c58892d0e66f325c))
+* fix task inbox shortcuts ([9bc949f](https://github.com/macite/doubtfire-deploy/commit/9bc949fd6dcb71ea165e89de9a4ec9d9347e6562))
+* inbox search box width ([02a99a8](https://github.com/macite/doubtfire-deploy/commit/02a99a8f71033014b26260d3b0dbfca075ca7273))
+* remove pdf viewer on task view when no sheet ([f3e973d](https://github.com/macite/doubtfire-deploy/commit/f3e973dacc5b079ab58da239194d802c5586ca07))
+* revert index changes, html changes for grade ([8f41cd1](https://github.com/macite/doubtfire-deploy/commit/8f41cd1656971963c2035a9249acdde0a257766e))
+* task list scrolling ([0088e9e](https://github.com/macite/doubtfire-deploy/commit/0088e9e245dbd326dea4032239b53eee88754179))
+* use tailwind classes instead of css styling ([42434a5](https://github.com/macite/doubtfire-deploy/commit/42434a5fd4b866781da5d23ecdb0b9b4369aace1))
+
+### [8.0.7](https://github.com/macite/doubtfire-deploy/compare/v8.0.6...v8.0.7) (2024-05-26)
+
+
+### Bug Fixes
+
+* add task description editing ([ee0cfbf](https://github.com/macite/doubtfire-deploy/commit/ee0cfbf7ce804a1806ca5810bad138b97703ebfe))
+
+### [8.0.6](https://github.com/macite/doubtfire-deploy/compare/v8.0.5...v8.0.6) (2024-05-25)
+
+
+### Bug Fixes
+
+* ensure all admin units are fully loaded ([917325e](https://github.com/macite/doubtfire-deploy/commit/917325ed08b89690a7eea0716438673732ba1ec9))
+
+### [8.0.5](https://github.com/macite/doubtfire-deploy/compare/v8.0.4...v8.0.5) (2024-05-25)
+
+
+### Bug Fixes
+
+* ensure (click) is on button, not mat-icon ([99c717d](https://github.com/macite/doubtfire-deploy/commit/99c717d2333de0975da1b3f282195a0b3577a3f6))
+
+### [8.0.4](https://github.com/macite/doubtfire-deploy/compare/v8.0.3...v8.0.4) (2024-05-25)
+
+
+### Bug Fixes
+
+* fix various issues with admin unit list ([3720666](https://github.com/macite/doubtfire-deploy/commit/3720666fb4105bac274449eb468ce34e0f68bae7))
+
+### [8.0.3](https://github.com/macite/doubtfire-deploy/compare/v8.0.2...v8.0.3) (2024-05-25)
+
+
+### Features
+
+* add 4-character abbreviations ([eb62dfc](https://github.com/macite/doubtfire-deploy/commit/eb62dfc90db836870d6c396727fc43c149808e9d))
+
+### [8.0.2](https://github.com/macite/doubtfire-deploy/compare/v8.0.1...v8.0.2) (2024-05-25)
+
+
+### Bug Fixes
+
+* gradeservice bugs ([fd6eb1c](https://github.com/macite/doubtfire-deploy/commit/fd6eb1cd152b72cf297cbd6bd529579858afc543))
+* gradeservice bugs ([7545eb9](https://github.com/macite/doubtfire-deploy/commit/7545eb92b9353d99ac8d8d7d89982cdadf9688b1))
+
+### [8.0.1](https://github.com/macite/doubtfire-deploy/compare/v8.0.0...v8.0.1) (2024-05-24)
+
+
+### Features
+
+* implement live PDF search that updates dynamically as you type ([b9b9945](https://github.com/macite/doubtfire-deploy/commit/b9b9945346d55392e5ee9a6431d85ae92de1bde5))
+
+
+### Bug Fixes
+
+* fix critical bugs ([356846e](https://github.com/macite/doubtfire-deploy/commit/356846e5c385c484a6034553366965cde95ec78b))
+* fix splashscreen ([2692b7f](https://github.com/macite/doubtfire-deploy/commit/2692b7f4a3e1ca2a5bc4df3b82a9dfd4aa2ec68a))
+
+## [8.0.0](https://github.com/macite/doubtfire-deploy/compare/v8.0.0-6...v8.0.0) (2024-05-23)
+
+
+### Features
+
+* add multi-badge support ([44c1473](https://github.com/macite/doubtfire-deploy/commit/44c1473700a3d7a4a025d68acac5aa61189d8b52))
+* add synchronised multi-badge ([f70ebdc](https://github.com/macite/doubtfire-deploy/commit/f70ebdca92a05e5ca350d4062a8dc8efba9fd681))
+* add working unit-badge ([dd30247](https://github.com/macite/doubtfire-deploy/commit/dd302478e97228e6bfbfdbee0134abb3eabf2587))
+* add working unit-badge ([c7338ff](https://github.com/macite/doubtfire-deploy/commit/c7338ffd88edb457a3443b43ea21706c43231973))
+* qol improvements for unit codes ([f7f928e](https://github.com/macite/doubtfire-deploy/commit/f7f928e485448a69b054dca94f07d623f84e328f))
+* small fixes to home and unit code ([918e804](https://github.com/macite/doubtfire-deploy/commit/918e8047745b489aae1fafdc67720568dd8d829e))
+
+
+### Bug Fixes
+
+* add missing imports ([659b52c](https://github.com/macite/doubtfire-deploy/commit/659b52c82a26b9496a3dba81401b5147d05248ae))
+* change time exceeded to feedback exceeded ([673619d](https://github.com/macite/doubtfire-deploy/commit/673619d99f177d834fba3643171426092e0d05e0))
+* correct task resource upload actions ([5e9c9b1](https://github.com/macite/doubtfire-deploy/commit/5e9c9b1d05d6d6975fd4ba3a3176314ddd7d8514))
+* ensure loading state changed after projects and unit roles load ([9171d0c](https://github.com/macite/doubtfire-deploy/commit/9171d0c2c37c17d26f464f6aa0ba7c479851e6eb))
+* ensure staff task list checks unit exists ([dbb312d](https://github.com/macite/doubtfire-deploy/commit/dbb312dc3c5c9e2be24161914e8d95e1a5c1bc53))
+* ensure unit load updates unit object if it already exists ([e48ffad](https://github.com/macite/doubtfire-deploy/commit/e48ffadf63891212c3e40ab696d54a3935485b08))
+* hide overseer settings if automation disabled ([0c08fe3](https://github.com/macite/doubtfire-deploy/commit/0c08fe3e82a78dd1e91fb44ce6008fb5298c46ee))
+* slide unit code off left side ([8cd57b5](https://github.com/macite/doubtfire-deploy/commit/8cd57b589812fd59c6da4de02246791fb4410959))
+* sort units desc by start date ([9b78856](https://github.com/macite/doubtfire-deploy/commit/9b788561372a3ddf065d3b28e547a2194ef29655))
+* zip upload for overseer ([5ad2085](https://github.com/macite/doubtfire-deploy/commit/5ad2085b2d730235f61e7b7c603727d8b6736cf2))
+
+## [8.0.0-6](https://github.com/macite/doubtfire-deploy/compare/v8.0.0-5...v8.0.0-6) (2024-05-13)
+
+## [8.0.0-5](https://github.com/macite/doubtfire-deploy/compare/v8.0.0-4...v8.0.0-5) (2024-05-13)
+
+## [8.0.0-4](https://github.com/macite/doubtfire-deploy/compare/v8.0.0-3...v8.0.0-4) (2024-05-11)
+
+
+### Features
+
+* add task badge ([b48cd4c](https://github.com/macite/doubtfire-deploy/commit/b48cd4c99333e41c0c1d32531e571d32bb482a81))
+
+
+### Bug Fixes
+
+* (WIP) fix alert service migration ([3d9f4dc](https://github.com/macite/doubtfire-deploy/commit/3d9f4dc05e7a45884dc383e0f5ed4bdb09b0558d))
+* add max length validator and change reason field to text-area ([6227200](https://github.com/macite/doubtfire-deploy/commit/6227200566c3f608c279e92a3f255a7e94148c1d))
+* address linter issues in task.ts ([14e9699](https://github.com/macite/doubtfire-deploy/commit/14e9699bdfe336f1e3f3cce3d722c835d58f3cdb))
+* copy dist browser to nginx html ([08fa919](https://github.com/macite/doubtfire-deploy/commit/08fa91937e028cb3ee093709e5a31f94da44fc33))
+* correct filenames for grotesk fonts ([9bedd81](https://github.com/macite/doubtfire-deploy/commit/9bedd812b12628df9cb32e48cb91a88b5cdf69b1))
+* ensure eula state remains visible ([131540c](https://github.com/macite/doubtfire-deploy/commit/131540ca4b934773afd9f72aa2578579048ed6a2))
+* ensure unit admin shows all units ([02b7a15](https://github.com/macite/doubtfire-deploy/commit/02b7a15bbbd2306a61e344f995dffb72a3915498))
+* fix course progress bar on firefox ([ae5c1e0](https://github.com/macite/doubtfire-deploy/commit/ae5c1e0b5901bae0411cf7bbaa32ee8f340b908a))
+* fix incorrect projcets listed in dropdown ([6a7e478](https://github.com/macite/doubtfire-deploy/commit/6a7e4788b522e5af6331a82ed33e9c9137d2ca86))
+* fix margin issue on dropdown ([c8145f0](https://github.com/macite/doubtfire-deploy/commit/c8145f0972a03e52377e0203cabb2cf6353db8b3))
+* grade and quality point display on task-assessment-card ([7cba502](https://github.com/macite/doubtfire-deploy/commit/7cba502e002143e8255bf0d786551bd270f73f0e))
+* indicate mandatory fields in upload-submission-modal ([e8801e2](https://github.com/macite/doubtfire-deploy/commit/e8801e2a64fe15ad423d9017d10e5e23f6e00eda))
+* only check grade if it exists ([1c213c5](https://github.com/macite/doubtfire-deploy/commit/1c213c53571f79563e9a071e2dd9645598382522))
+* remove unused imports in extension-modal.service ([8081f51](https://github.com/macite/doubtfire-deploy/commit/8081f51b5f28ea511b9f76d026ae63f16153ae2c))
+* use tailwindcss classes ([8d7e160](https://github.com/macite/doubtfire-deploy/commit/8d7e160e2d40c42284ee0c1fc85d6d9e01166cda))
+
+## [8.0.0-3](https://github.com/macite/doubtfire-deploy/compare/v8.0.0-2...v8.0.0-3) (2024-05-02)
+
+
+### Bug Fixes
+
+* reenable eula html fetch ([cda9e52](https://github.com/macite/doubtfire-deploy/commit/cda9e52a259bfaa27e8252f40e579d11d1af280c))
+
+## [8.0.0-2](https://github.com/macite/doubtfire-deploy/compare/v7.0.21...v8.0.0-2) (2024-05-01)
+
+
+### Features
+
+* accept promela files ([df93a69](https://github.com/macite/doubtfire-deploy/commit/df93a69e45d2d9e41b9cf0802d521a3663d7a36c))
+* migrate unit list in all locations ([6651d00](https://github.com/macite/doubtfire-deploy/commit/6651d0011446fdde7179c5173f902ce2e4ea7545))
+* remove misleading progress statement ([63e276c](https://github.com/macite/doubtfire-deploy/commit/63e276c041f77d8b401d24a4f49896cfa2095442))
+
+
+### Bug Fixes
+
+* correct change detection on task def ([55daff2](https://github.com/macite/doubtfire-deploy/commit/55daff234441a242d96de097c2b2912df0c6c9a8))
+* correct npm install for dev container image ([5126c3b](https://github.com/macite/doubtfire-deploy/commit/5126c3ba7fed55217e88c54c25dc70595ec4c666))
+* ensure npm will force install ([f91751e](https://github.com/macite/doubtfire-deploy/commit/f91751eae788bc3f65d33b610ed80c96d3978974))
+* fix firefox-specific bug ([6b6054b](https://github.com/macite/doubtfire-deploy/commit/6b6054bd5c1104a96ccac413a7115009eeba8d2f))
+* remove ngClass ([d1d8c72](https://github.com/macite/doubtfire-deploy/commit/d1d8c72ace21551a68bd4d0a8ba88eabb2ebdb9d))
+* remove plagiarism checks ([21b02fb](https://github.com/macite/doubtfire-deploy/commit/21b02fb7cc616090f1fb2d26f65b336072f9105b))
+* remove the incorrect option from for scrollIntoView() ([9e752fe](https://github.com/macite/doubtfire-deploy/commit/9e752fe9d49df786ceb3e7007cbf0aad57809604))
+* remove unused loading from f-units ([c4c38fa](https://github.com/macite/doubtfire-deploy/commit/c4c38fad4fbdfe248e383cf247f949197ad0a047))
+* typo ([820c56f](https://github.com/macite/doubtfire-deploy/commit/820c56f3a1bc30b7637b98e6f80b406ca350b5b8))
+
+## [8.0.0-1](https://github.com/macite/doubtfire-deploy/compare/v8.0.0-0...v8.0.0-1) (2024-03-21)
+
+
+### Bug Fixes
+
+* update deploy dockerfile ([41a5161](https://github.com/macite/doubtfire-deploy/commit/41a5161b356931eafd8e4caf256f311f10bc8bb4))
+
+## [8.0.0-0](https://github.com/macite/doubtfire-deploy/compare/v7.0.18...v8.0.0-0) (2024-03-21)
+
+
+### Features
+
+* (wip) add similarities notification ([8e4a198](https://github.com/macite/doubtfire-deploy/commit/8e4a198a037de832bbebe96e309c4c8485d076cc))
+* add ability to accept tii eula to model ([0106b01](https://github.com/macite/doubtfire-deploy/commit/0106b01ed249139cbe06096f95a36b5d11bcd0ac))
+* add ability to accept turn it in eula ([380ad1e](https://github.com/macite/doubtfire-deploy/commit/380ad1e93252f8f928893626b65f4dab01813524))
+* add ability to open turnitin viewer ([5248e80](https://github.com/macite/doubtfire-deploy/commit/5248e80622e55b79ce4d7b35f8202ee8099c2dce))
+* add ability to update similarity flag ([de005ee](https://github.com/macite/doubtfire-deploy/commit/de005ee98a270998e2175109783cab72b92e36f3))
+* add dynamic footer ([3c62fe0](https://github.com/macite/doubtfire-deploy/commit/3c62fe06d009c3b752259f50887c625c98a371cd))
+* add empty pdf view to new task dashboard ([9c3cdfb](https://github.com/macite/doubtfire-deploy/commit/9c3cdfbb93711a6ae911ed461651200d8c75db48))
+* add file drop component ([c211003](https://github.com/macite/doubtfire-deploy/commit/c211003004ae6c7505158a65ce2006b219920827))
+* add footer content ([735c43b](https://github.com/macite/doubtfire-deploy/commit/735c43b5c2df7e8f367310ad92ef716fd5140375))
+* add link to student task from inbox ([d4301e0](https://github.com/macite/doubtfire-deploy/commit/d4301e0d1f2f6ec4c563801a7f0d1132884f9989))
+* add mobile inbox view ([56ded70](https://github.com/macite/doubtfire-deploy/commit/56ded707e638ae769665a863ce65acffea5a588a))
+* add multi-file support to f-upload ([d181969](https://github.com/macite/doubtfire-deploy/commit/d181969198e2b7611e3f0e330577b825e99a248f))
+* add new file upload component ([891ab20](https://github.com/macite/doubtfire-deploy/commit/891ab20eb6f1d545ff58d0157fdd9912b9546c9f))
+* add new inbox component ([ec0f62c](https://github.com/macite/doubtfire-deploy/commit/ec0f62c488ae3e99e9487ea1ab87f5bb2f1fbed7))
+* add new pdf-viewer ([617fbd2](https://github.com/macite/doubtfire-deploy/commit/617fbd24f8424541aeb87b16651f06737b570633))
+* add new similarity component ([48ba01c](https://github.com/macite/doubtfire-deploy/commit/48ba01c8d780a04a1f526f89fcd9bc63ef5d4aec))
+* add new task assessment card ([ac3b50f](https://github.com/macite/doubtfire-deploy/commit/ac3b50f29b8a2722d09ad94a2f82bb54c1c32da7))
+* add new task dashboard component ([dc841f9](https://github.com/macite/doubtfire-deploy/commit/dc841f9e7af811a3a56a0c21fd3b3968ec7aead9))
+* add new task due card ([3ade5ea](https://github.com/macite/doubtfire-deploy/commit/3ade5ea34eb3c2c756d7df0f7a6c250fa50290bb))
+* add new task due card content ([c43ea3e](https://github.com/macite/doubtfire-deploy/commit/c43ea3e27abdf253596bfedd5112f5ef7a7b929d))
+* add new task submission card ([6d27690](https://github.com/macite/doubtfire-deploy/commit/6d2769029413f84b73c7c825d16e9a3366142177))
+* add pdf search and zoom ([f83ce57](https://github.com/macite/doubtfire-deploy/commit/f83ce570e80dc20d118288c6d32c2d2a9ca263b5))
+* add resizable inbox panels ([4920e31](https://github.com/macite/doubtfire-deploy/commit/4920e31ce366c3dfb4340ab4da159b176381cf49))
+* add resizable panels to inbox ([5abda03](https://github.com/macite/doubtfire-deploy/commit/5abda039af5b2d3b5e2589c4864b2e1715c5e8c0))
+* add rest of layout to footer ([3b9de7c](https://github.com/macite/doubtfire-deploy/commit/3b9de7cc6d923a0e6431524f2967b5e3bc360a6f))
+* add selected task sheet url ([00a7fb5](https://github.com/macite/doubtfire-deploy/commit/00a7fb516d362d3725b915d012878a9ccf8ef009))
+* add selectedTask service ([02aa41a](https://github.com/macite/doubtfire-deploy/commit/02aa41a34232359d1366d841d1236a068a837ae5))
+* add similarities staff dashboard switch ([1ec0b72](https://github.com/macite/doubtfire-deploy/commit/1ec0b72c26cc27bd4d17376af5b47650d28bbc10))
+* add similarity flags to view ([8c7975c](https://github.com/macite/doubtfire-deploy/commit/8c7975cadd14e6d3390cfa9917983cd01498a69c))
+* add simple project progress bar ([73fda77](https://github.com/macite/doubtfire-deploy/commit/73fda776ee36c6574c1ddc0dc09b958d61de3000))
+* add start of new similarity view ([a1e51b6](https://github.com/macite/doubtfire-deploy/commit/a1e51b6bc7030086a1d7a4d715f23b3e76f7ddb3))
+* add task plagairism warning on inbox ([6bd8620](https://github.com/macite/doubtfire-deploy/commit/6bd8620f5473db4517e38fd0487ab5876f921a36))
+* add teaching period remove break function ([90c6a37](https://github.com/macite/doubtfire-deploy/commit/90c6a3730f84da9e64abec676bcdf66c10b7fc60))
+* add tii action log ([96d982d](https://github.com/macite/doubtfire-deploy/commit/96d982d5fb945bdbf9ea8b6d534a5dd18ab64ad7))
+* add unselected user icon badge in inbox ([794697b](https://github.com/macite/doubtfire-deploy/commit/794697b53d6ee81f57b37b173d03a0688c6993b5))
+* complete mobile task inbox view ([00a4b3f](https://github.com/macite/doubtfire-deploy/commit/00a4b3f1e8f52364a83b6affae34295da4a7af34))
+* disable hover on task inbox for mobile ([a59fd01](https://github.com/macite/doubtfire-deploy/commit/a59fd0153de8b1f9e9b9022d0f6df1032c92f8d1))
+* downgrade task due card ([c342ac2](https://github.com/macite/doubtfire-deploy/commit/c342ac2e3cc4213c2b92499bc4a191485184af37))
+* enhance task search with similarity flags ([65ad918](https://github.com/macite/doubtfire-deploy/commit/65ad91842882e76379033350f90b966d874c6ba4))
+* enhance ui/ux of task inbox screen ([6b275c3](https://github.com/macite/doubtfire-deploy/commit/6b275c3cd5d575a399e901d52a62808238c6d0e4))
+* expand new file uplaoder ([11d798d](https://github.com/macite/doubtfire-deploy/commit/11d798d6a6e5eca4555a7d141e1d11d2a862fcfe))
+* finish new alerts service ([3beb059](https://github.com/macite/doubtfire-deploy/commit/3beb05987e605c9f4270fc0d7bdd95f82bf64007))
+* make all datetime in aus format ([7a0f749](https://github.com/macite/doubtfire-deploy/commit/7a0f749a4328a44c1ffb5692017dfa39a061e1d5))
+* migrate new unit dialog ([0a2a1e9](https://github.com/macite/doubtfire-deploy/commit/0a2a1e9baca6b0553730c1a939b246bf1be178a3))
+* minor improvements to file-drop ([73139be](https://github.com/macite/doubtfire-deploy/commit/73139be83449f50036eedb3520ed2d8daa5da09c))
+* new footer component ([c3b4c18](https://github.com/macite/doubtfire-deploy/commit/c3b4c182921079d6ebc0851ec7e6515e65f1c15b))
+* new grade service ([31073e1](https://github.com/macite/doubtfire-deploy/commit/31073e1dbe963c1e0f2c84139a2c3f746cc31bbb))
+* new student user badge ([97c84d3](https://github.com/macite/doubtfire-deploy/commit/97c84d3016b0e322d54062fae0d46da112146d5d))
+* new task status card ([78218cf](https://github.com/macite/doubtfire-deploy/commit/78218cf6d45f5cac50203f96109d27400fa37686))
+* only download similarity resource on view ([563d102](https://github.com/macite/doubtfire-deploy/commit/563d1024ee7023e4228a9ceef9f556e48e7127d1))
+* progress redesigned inbox ([f1b672b](https://github.com/macite/doubtfire-deploy/commit/f1b672b7495a9086abb88f5e1991cb4c4f7b60c1))
+* register new inbox component ([3d79baa](https://github.com/macite/doubtfire-deploy/commit/3d79baae505e44c401179ae34de4804e8d90b7ba))
+* show TII accepted status in profile ([7b5c3e2](https://github.com/macite/doubtfire-deploy/commit/7b5c3e2c0fe9fcc30f9fffc73d9a942714db7cb2))
+* small change to task view ([f640920](https://github.com/macite/doubtfire-deploy/commit/f6409201e9d4af4f075b4db8559fc1e26f4a374f))
+* small changes to user table ([18957ac](https://github.com/macite/doubtfire-deploy/commit/18957acfc700e941ebb161d9ddb8f5e1ebe1beff))
+* trigger window resize when pdf loads ([044c170](https://github.com/macite/doubtfire-deploy/commit/044c170ebe05e7e3a368df2e8041884d2e23c752))
+* update file viewer to use file download and blobs ([8751123](https://github.com/macite/doubtfire-deploy/commit/87511232ca384a355ddfc5eb396186babd676f92))
+* wip add file uploader component ([48c73f5](https://github.com/macite/doubtfire-deploy/commit/48c73f547892ae3ce12fdac416bf93c3a2c72006))
+
+
+### Bug Fixes
+
+* add activeUntil date mapping to tps ([2cae20a](https://github.com/macite/doubtfire-deploy/commit/2cae20a6b3fa3e5ea8a001ffa40a87263c438b68))
+* add arbitrary task argument to `togglePin` in task inbox ([6e1784f](https://github.com/macite/doubtfire-deploy/commit/6e1784ff52a745330e57f2ccc2a55b3206d535d0))
+* add seperator to naviation on mobile ([fc4f052](https://github.com/macite/doubtfire-deploy/commit/fc4f0527b144fd8f3b6e98b335e629f85fe1bb3b))
+* add the correct enterkeyhint to the comment composer ([569db05](https://github.com/macite/doubtfire-deploy/commit/569db059fc9786363b1b62d7be72e44f711faba3))
+* add unloaded state to progressbar ([2c3a28e](https://github.com/macite/doubtfire-deploy/commit/2c3a28e9718879f55596221517db35bda8c09450))
+* adjust due card layout ([0360a6e](https://github.com/macite/doubtfire-deploy/commit/0360a6e0868e995d7ba5c9091be556dad4bfd474))
+* always display shortened task name in header ([a05c8ff](https://github.com/macite/doubtfire-deploy/commit/a05c8ffd9aef9c865f72458157b465f9de1e07f3))
+* center pins along items in new task inbox ([6b3c0d2](https://github.com/macite/doubtfire-deploy/commit/6b3c0d2b1d5541fe36c6fbd03dfc752f43d03e18))
+* center placeholder text in task inbox search bar & reduce padding on far-right ([ffec9b6](https://github.com/macite/doubtfire-deploy/commit/ffec9b6f4bfc9d2aee6aa762c41bcce1ed1823ba))
+* center student and task name on bottom left screen ([e986199](https://github.com/macite/doubtfire-deploy/commit/e9861992e9e5a40076973a2215bcc919c1777b5d))
+* center view all units button on home page ([9807dfd](https://github.com/macite/doubtfire-deploy/commit/9807dfd1f18e1efc9f882a67ef99f1d198a03ff7))
+* centering of Administration icons on home page ([1043f74](https://github.com/macite/doubtfire-deploy/commit/1043f74e2ca5d23cad8c4d890a202b20ddb4bc37))
+* change display name of student on bottom left screen from being nickname to full name ([5fe7790](https://github.com/macite/doubtfire-deploy/commit/5fe7790e23aec709f8be5a444bf84ebdf77c9ce5))
+* change shown name on messages in chat to be student's full name ([93636c4](https://github.com/macite/doubtfire-deploy/commit/93636c400c1850e20320a57be010347541c5137c))
+* chatbox spacing, placement of messages <> photos, distance between message & name ([dd9cb2a](https://github.com/macite/doubtfire-deploy/commit/dd9cb2ab3bdde886c2f297d8044986427febd366))
+* ensure accepted TII eula details are retained ([e60ae54](https://github.com/macite/doubtfire-deploy/commit/e60ae547121f33edd7cc8d8c5d4afd27a1f90e8c))
+* ensure blobs are freed on destroy ([ee017eb](https://github.com/macite/doubtfire-deploy/commit/ee017eb00a506c81a108ce6dec52f7f91064d350))
+* ensure font is set to roboto ([a6ff4ce](https://github.com/macite/doubtfire-deploy/commit/a6ff4cee9794e37a208af28d7d085182441fa555))
+* ensure loadedUnitRoles is public in global ([b92159f](https://github.com/macite/doubtfire-deploy/commit/b92159ff33cf9537c0ccec50d57a54636315bec4))
+* ensure logout in timeout always signs out ([3380075](https://github.com/macite/doubtfire-deploy/commit/33800759083156682014f0f63bc83a0283ae96fa))
+* ensure mat menu icons have correct tags ([f971ad1](https://github.com/macite/doubtfire-deploy/commit/f971ad1cf7fa56de0f4e97e9533a0567db897da4))
+* ensure only staff need to accept turn it in EULA ([029c40b](https://github.com/macite/doubtfire-deploy/commit/029c40b556bb1709e75547615b2d1a1109997a74))
+* ensure pdf actions loads after pdf loaded ([0425c54](https://github.com/macite/doubtfire-deploy/commit/0425c5465a678509e1a8d719fef39975c4d8e9e5))
+* ensure similarities without files still view ([5794e2f](https://github.com/macite/doubtfire-deploy/commit/5794e2fb345328396517a7d2f945cfabb1c37173))
+* ensure task pdf viewer works ([d3d1bf8](https://github.com/macite/doubtfire-deploy/commit/d3d1bf8564c6e6134130b44df90bef42430f4654))
+* ensure task submission status updated on init ([ca77c92](https://github.com/macite/doubtfire-deploy/commit/ca77c92d251bf4087964886579de642f6f300b9a))
+* ensure that timeout state not re-routed ([c16ee90](https://github.com/macite/doubtfire-deploy/commit/c16ee90c2c372ea1889d3470e2be510aefd2245f))
+* ensure welcome page still can go to timeout ([5253f2e](https://github.com/macite/doubtfire-deploy/commit/5253f2eb6fce1de1b6df585589f1cc2594ac87a8))
+* excessive spacing below unit tags on home page ([1bfaa21](https://github.com/macite/doubtfire-deploy/commit/1bfaa216440d880d82875f519e9b3f0f59ddcc23))
+* fix few type errors ([8a96e5b](https://github.com/macite/doubtfire-deploy/commit/8a96e5b33a095cd70222719b33d7dc853d67f347))
+* fix file drop to support click to upload ([599cd9e](https://github.com/macite/doubtfire-deploy/commit/599cd9e82c1518113374f0675fc54dd1483fcc85))
+* fix footer height not resetting on similarity ([19c43d7](https://github.com/macite/doubtfire-deploy/commit/19c43d7945c5848d1ca511e146fefdfa9735c49b))
+* fix incorrect closing tag ([ad28818](https://github.com/macite/doubtfire-deploy/commit/ad28818fba9dac552a2adf14785c5bbf98b4e206))
+* fix page height recalculation trigger ([8f219c3](https://github.com/macite/doubtfire-deploy/commit/8f219c31e9a3461626a9344352c4d7aa68c57c43))
+* fix various build issues ([8b947ae](https://github.com/macite/doubtfire-deploy/commit/8b947aeea1cb0dfa4bac8e22ff5bdb2a9a82ec49))
+* fix various task comment composer/display issues ([5670bf1](https://github.com/macite/doubtfire-deploy/commit/5670bf1dcd77e8010fe88bf24662ec96e4e29847))
+* hide extension for staff if can't apply ([1699e50](https://github.com/macite/doubtfire-deploy/commit/1699e50f2d7424361e0f7d5a173bdd9fffd87c24))
+* initialise inbox on return ([1249e96](https://github.com/macite/doubtfire-deploy/commit/1249e96f53ef9544cd81e3f2c3f1d5acf4a628f3))
+* initialise inbox view ([1ae4ac9](https://github.com/macite/doubtfire-deploy/commit/1ae4ac9b304348f5528368d87ded3c26e8e5cf40))
+* issues with UI in photo spacing and text alignment ([716ead3](https://github.com/macite/doubtfire-deploy/commit/716ead3497a8a2ecd1050484bd289c003a47dffa))
+* make photo in chatbox fall to bottom ([aa4e814](https://github.com/macite/doubtfire-deploy/commit/aa4e81446546f1e62b08fd74ba8ac1e563e5efc8))
+* make read receipt smaller to fix message alignment ([c869458](https://github.com/macite/doubtfire-deploy/commit/c869458cd45334e009ae83ab3c911033bc1987b9))
+* null input checks on migrated cards ([7a192af](https://github.com/macite/doubtfire-deploy/commit/7a192af22bf1eef026d05a16edcc3a97c6abbebf))
+* pass selected task to footer's user icon ([28782b1](https://github.com/macite/doubtfire-deploy/commit/28782b134cb03f5c334df48e8887f81393661bda))
+* photo from below message to being inline ([0e97a03](https://github.com/macite/doubtfire-deploy/commit/0e97a03a491f4137ec301dd18a42130a845ebe37))
+* reduce user badge clickable region ([7305f86](https://github.com/macite/doubtfire-deploy/commit/7305f866a9582e10dd870fad1861de4ce209a4e0))
+* regenerate user icons on input changes ([257ab4e](https://github.com/macite/doubtfire-deploy/commit/257ab4eb63ec15de545193f8b60176ac33e17b01))
+* remove call to teaching period name ([1e2e019](https://github.com/macite/doubtfire-deploy/commit/1e2e0198a32acd59c11932fde2a2f14a5bacf48f))
+* remove dupe case in transition hook ([9808798](https://github.com/macite/doubtfire-deploy/commit/98087981e06296a1eb9a17f527c49ab9b27caef3))
+* remove incorrect aria hidden tags ([46bfe21](https://github.com/macite/doubtfire-deploy/commit/46bfe2109e4b912a146201572671ff759fc02226))
+* remove no PDF text from overlapping with icon ([c6543cf](https://github.com/macite/doubtfire-deploy/commit/c6543cf2f9be9644b7382eeb64699e15e461447a))
+* remove old teaching periods from home ([4f4a617](https://github.com/macite/doubtfire-deploy/commit/4f4a6175f39ac9df75c7efe920ffcc15576cc98e))
+* remove pct copy data ([6bb96b4](https://github.com/macite/doubtfire-deploy/commit/6bb96b49f436a7d4bd6f5e587b30a8528e6d75b7))
+* remove user badge from old task panel ([9129956](https://github.com/macite/doubtfire-deploy/commit/9129956f41b78a8e3e84ffe9a124a7941d8905c1))
+* require eula for any user ([daf7ba9](https://github.com/macite/doubtfire-deploy/commit/daf7ba9d9056c262cafc871b623c59a0ef837274))
+* resolved issue with intelligent discussion ([18cd1fc](https://github.com/macite/doubtfire-deploy/commit/18cd1fc6f0443c353fc5f49c81dafd2428901cbc))
+* resolved issue with intelligent-discussion-dialog component rendering ([1efa758](https://github.com/macite/doubtfire-deploy/commit/1efa758fd813751788cc79fe30e01ca8a09013d8))
+* shrink header menu on small screens ([580445d](https://github.com/macite/doubtfire-deploy/commit/580445d25573bf00671246269c61685917c34867))
+* spacing (of own/person messaging) is now right/left aligned respectively ([7d10aa0](https://github.com/macite/doubtfire-deploy/commit/7d10aa0f58e2d7c8e8fd45439d8c464fabe3508c))
+* split header onto seperate lines on small screens ([d8a9016](https://github.com/macite/doubtfire-deploy/commit/d8a90168e58ef8d71f31f76d999d4f6a59232987))
+* switch max pct to similarity flag in project ([094b418](https://github.com/macite/doubtfire-deploy/commit/094b418c1bff709e755bf7c61207ebf22bfc6106))
+* task explorer 405 error ([7ea516c](https://github.com/macite/doubtfire-deploy/commit/7ea516ce8e346d46e8acf68c8b41fa81485515ba))
+* task submission pdf downloading ([b0becb4](https://github.com/macite/doubtfire-deploy/commit/b0becb4c76e4e45f10782561a6b53a322eb43665))
+* tweak theme.scss file ([393770b](https://github.com/macite/doubtfire-deploy/commit/393770b35d1688d27bce47e1d6b94735a1822494))
+* update package lock ([674f3c7](https://github.com/macite/doubtfire-deploy/commit/674f3c70bd0a4c8c33e25d34beeab76c29f56325))
+* use angular 15 ([d0104cc](https://github.com/macite/doubtfire-deploy/commit/d0104cc57edda68a1a165bdf1fd7cf75bd74ec68))
+* use correct task object in task pinning ([a5fceb0](https://github.com/macite/doubtfire-deploy/commit/a5fceb048b931dbd5ccc3ad5d4fd898b146284fb))
+* use correct theming import ([b0d3232](https://github.com/macite/doubtfire-deploy/commit/b0d3232eaed2096296cf025437dda26ddcd595e0))
+* use disabled blinding ([628ca55](https://github.com/macite/doubtfire-deploy/commit/628ca55b4e8f569f751b78ead7a50fa9e086ccec))
+* use new state name in task dropdown ([3c2fe44](https://github.com/macite/doubtfire-deploy/commit/3c2fe444d398bcedb5c998a47683d04279996680))
+* use onChanges rather than input setter ([8069c21](https://github.com/macite/doubtfire-deploy/commit/8069c21d9ecfc412dea37cade8e8a37e79730f01))
+
### [8.0.9](https://github.com/doubtfire-lms/doubtfire-deploy/compare/v8.0.8...v8.0.9) (2024-05-31)
@@ -328,8 +2162,15 @@ All notable changes to this project will be documented in this file. See [standa
* use tailwindcss classes ([8d7e160](https://github.com/macite/doubtfire-deploy/commit/8d7e160e2d40c42284ee0c1fc85d6d9e01166cda))
-### [7.0.23](https://github.com/macite/doubtfire-deploy/compare/v7.0.22...v7.0.23) (2024-06-04)
+### [7.0.24](https://github.com/macite/doubtfire-deploy/compare/v7.0.23...v7.0.24) (2024-06-05)
+
+### Bug Fixes
+* ensure download blob supports 206 responses ([6445f9f](https://github.com/macite/doubtfire-deploy/commit/6445f9f998db70fbe9b9abf723dc17fd298b5d2f))
+
+
+
+### [7.0.23](https://github.com/macite/doubtfire-deploy/compare/v7.0.22...v7.0.23) (2024-06-04)
### Bug Fixes
diff --git a/CROSS_UNIT_STATUS.md b/CROSS_UNIT_STATUS.md
new file mode 100644
index 0000000000..b83cfc162e
--- /dev/null
+++ b/CROSS_UNIT_STATUS.md
@@ -0,0 +1,41 @@
+# Cross-Unit (Cross-Project) Dashboard — starting-point status
+
+**Branch:** `feature/cross-unit` (off `11.0.x`) · **Repos:** `doubtfire-web` + `doubtfire-api`
+**State:** skeleton ported onto v11.0 and wired — *not yet built/tested* (see Verification).
+
+## What this feature is
+A single dashboard that shows a student's tasks across **all** their units in one view
+(`/dashboard`, Student-only). Currently a bare unit→task list — the visual/UX build sits on top.
+
+## What landed in this port
+Ported from the old `Feature/Cross-Unit` branch (forked ~`10.0.0`, ~561 web / 169 api commits
+behind) and **re-homed onto v11.0** — not a git rebase.
+
+### doubtfire-web
+- **Clean adds (7 files):** `src/app/dashboard/**` — `f-cross-dashboard`, `list-item/dashboard-list-item`,
+ `list-item/expanded-list-item/*`. Verified to compile against v11's `GlobalStateService`
+ (`onLoad`, `currentUserProjects.values`).
+- **Wiring (4 edits), translated UI-Router → Angular Router:**
+ - `app.routes.ts` — new top-level `{path: 'dashboard', component: CrossDashboardComponent,
+ canActivate: [roleWhitelistGuard], data: {roleWhitelist: ['Student']}}`.
+ - `doubtfire-angular.module.ts` — registered the 3 components in `declarations`.
+ ⚠️ **Shared file — coordinate with `feature/notifications` and `peer-progress`.**
+ - `home/states/home/home.component.html` — added a "View all" (`/dashboard`) button beside
+ "View previous" (`/view-all-projects`); converted `uiSref` → `routerLink`.
+ - `projects/states/index/global-state.service.ts` — projects query now sends
+ `include_task_definitions: true` (also fixed the latent `include_in_active` → `include_inactive`
+ param typo so web matches the API).
+
+### doubtfire-api (`feature/cross-unit` branch there)
+- `app/api/projects_api.rb` — new `include_task_definitions` param on `GET /projects`.
+- `app/api/entities/project_entity.rb` — expose `tasks` when `include_task_definitions`.
+- `app/api/entities/minimal/minimal_unit_entity.rb` — expose `task_definitions` when requested.
+
+## Verification (NOT yet run — do before relying on this)
+- web: `npm ci && npm run build && npm run lint`
+- api: `bundle exec rubocop` + relevant tests
+- manual: log in as a Student → home shows the new button → `/dashboard` renders the unit list.
+
+## What the team builds on top
+Task status/colours, due dates, sorting/filtering, Material styling, empty/loading states.
+The port only gets the compiling skeleton onto v11 as a shared starting point.
diff --git a/DASHBOARD-SAMPLE-DATA.md b/DASHBOARD-SAMPLE-DATA.md
new file mode 100644
index 0000000000..2befa99d66
--- /dev/null
+++ b/DASHBOARD-SAMPLE-DATA.md
@@ -0,0 +1,10 @@
+# Cross-unit dashboard sample data
+
+These values are entirely synthetic and are intended only as a visual reference for dashboard
+mockups. They are not an API schema or an automated-test fixture. Dates use `DD/MM/YYYY`.
+
+| Unit code | Example task due date | Unit completion | Example task status |
+| --------- | --------------------- | --------------- | ------------------- |
+| COS10001 | 17/10/2026 | 10% | `not_started` |
+| COS20007 | 19/08/2026 | 100% | `complete` |
+| COS30046 | 25/08/2026 | 90% | `working_on_it` |
diff --git a/Dockerfile b/Dockerfile
index 18590c09e9..8818bb1674 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,8 +1,14 @@
-FROM node:20
+FROM node:22.23.2-bookworm-slim@sha256:83f487e0a63425e5b4d146fb5e5be574bcbe1b7b843d3ebafdd95eaf7767a7e5
-ENV DEBIAN_FRONTEND noninteractive
+# The upstream image ships npm 10.9.8. Upgrade to the latest Node 22-compatible
+# npm 11 release to pick up patched bundled dependencies before dropping
+# privileges.
+RUN npm install --global npm@11.19.1 \
+ && npm cache clean --force
+
+ENV DEBIAN_FRONTEND=noninteractive
ENV USER=node
-ENV NODE_ENV docker
+ENV NODE_ENV=docker
# You can not use `${USER}` here, but reference `/home/node`.
ENV PATH="/home/node/.npm-global/bin:${PATH}"
@@ -26,5 +32,5 @@ RUN npm ci --force --include=optional
EXPOSE 9876
-# Launch - install on launch so that node_modules are updated in volume
-CMD /bin/bash -c 'npm install; npm start'
+# Install on launch so that bind-mounted source and node_modules stay current.
+CMD ["/bin/bash", "-c", "npm install && exec npm start"]
diff --git a/JPlag-Report-Viewer b/JPlag-Report-Viewer
new file mode 160000
index 0000000000..4132c30d9e
--- /dev/null
+++ b/JPlag-Report-Viewer
@@ -0,0 +1 @@
+Subproject commit 4132c30d9ed5606df4648a7f1494ca751d3dca49
diff --git a/MIGRATION-GUIDE.md b/MIGRATION-GUIDE.md
index 049c5d2e52..3c3800c24f 100644
--- a/MIGRATION-GUIDE.md
+++ b/MIGRATION-GUIDE.md
@@ -57,7 +57,7 @@ Notice the naming convention. When migrating a component we use the format _name
Add the start of the typescript using something based on the following:
```typescript
-import { Component, Input, Inject } from '@angular/core';
+import {Component, Inject, Input} from '@angular/core';
@Component({
selector: 'task-description-card',
@@ -96,7 +96,6 @@ We want to make sure we can see our progress as quickly as possible. So lets sta
There are a few files we need to update to achieve this.
- Remove link to component from the angular module.
-
- Open the component's CoffeeScript file and make a note of the name of the module.
```coffeescript
@@ -111,7 +110,7 @@ There are a few files we need to update to achieve this.
- Setup the new component in **doubtfire-angular.module.ts**
- Import like this:
```ts
- import { TaskDescriptionCardComponent } from './projects/states/dashboard/directives/task-dashboard/directives/task-description-card/task-description-card.component';
+ import {TaskDescriptionCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-description-card/task-description-card.component';
```
- Then add the component name to the list of **declarations**. Now the component will be available in Angular.
- Remove the old and downgrade the new in **doubtfire-angularjs.module.ts**
@@ -124,24 +123,31 @@ There are a few files we need to update to achieve this.
```typescript
DoubtfireAngularJSModule.directive(
'taskDescriptionCard',
- downgradeComponent({ component: TaskDescriptionCardComponent })
+ downgradeComponent({component: TaskDescriptionCardComponent}),
);
```
- Update attributes on the new component usage.
-
- Search for all of the places where the component was already used (i.e. search for the component HTML tag).
- Update the property binding style to use the Angular form which is `[property]="value"`.
For example:
```html
-
+
+