Even mode=min still hits the registry's blob size limit (400 Bad Request).
The local runner's Docker daemon layer cache is sufficient for fast
incremental builds without needing a separate registry cache tag.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mode=max exports all intermediate layer blobs which exceeded the Forgejo
registry's blob upload size limit (400 Bad Request). mode=min only exports
the final image layers, keeping cache entries small enough for the registry.
The actual image build and push was succeeding; only the cache write failed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.logo-book fill was var(--color-text) which renders black in light mode.
Changed to var(--color-primary) so it matches the brand indigo in both
modes. Stroke updated to match at 70% opacity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Design direction: "Illuminated Transcript" — the AI response is the
primary artifact; user prompts are subordinate margin notes.
ChatMessage.vue:
- User bubble: ghosted background (rgba 3-4% opacity), muted text,
thin near-invisible border. De-emphasized, recessed.
- Assistant bubble: left indigo accent border (2px), elevated shadow
via --color-bubble-asst-shadow. Commands attention.
- Assistant role label: indigo (var(--color-primary)); user label muted.
- Removed old white-override content rules (no longer on dark bg).
- Thinking block: full border → left-only indigo accent bar (35% opacity),
removed inner top border.
ChatView.vue:
- Streaming bubble: same left accent + shadow as assistant bubbles.
- Streaming role label: indigo to match ChatMessage.
- Thinking block in streaming: same treatment as ChatMessage.
theme.css:
- Added --color-bubble-user-bg/border/text for light + dark.
- Added --color-bubble-asst-shadow for light + dark.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces docker-ce-cli (requiring external Docker apt repo) with
docker.io from Ubuntu 24.04 main, which is sufficient for docker login
and working alongside buildx.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Single combined layer was ~280MB and consistently failed to push with
499 (nginx closing connection). Split into three layers (~70-100MB each)
so each is pushed independently and stays within the timeout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docker/login-action requires the docker CLI binary in the job container.
The daemon is provided by the host socket mount; only the CLI is needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ubuntu 24.04 blocks pip install into the system Python by default.
Add --break-system-packages for the lint job's throwaway container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Labels are defined in act-runner-config.yml which is the authoritative
source. The env var is only used on first registration and was being
ignored after .runner was created.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The label now explicitly declares the dependency versions it provides
rather than using the opaque ubuntu-latest alias. All runs-on fields
in ci.yml updated to match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move ubuntu-latest → runner-base:py3.12-node22 mapping into
act-runner-config.yml so it is respected at runtime. The
GITEA_RUNNER_LABELS env var is only honoured on first registration
and was being ignored after the runner's .runner file was created.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- infra/Dockerfile.runner-base: new Ubuntu 24.04 image with Python 3.12
and Node 22 LTS pre-installed, tagged py3.12-node22. Eliminates the
~3-4 min deadsnakes PPA install that ran on every test job.
- infra/runner-compose.yml: update ubuntu-latest label to use
runner-base:py3.12-node22 instead of node:20-bullseye.
- .forgejo/workflows/ci.yml: remove Python 3.12 install steps (now in
base image), add actions/cache for pip keyed on pyproject.toml hash,
bump node-version 20 → 22.
- Dockerfile: bump frontend build stage node:20-alpine → node:22-alpine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three-layer guard on create_note and create_task:
1. Exact title match (get_note_by_title, case-insensitive) → hard block,
redirects model to use update_note instead
2. Fuzzy title match (SequenceMatcher ≥82%, punct-stripped word search for
candidates) → hard block; catches "Game Premise" vs "Game Premise Notes"
3. Semantic content similarity (semantic_search_notes threshold=0.87) → soft
block with requires_confirmation:true; model asks user then retries with
confirmed:true; graceful no-op if embedding model is unavailable
create_project and create_milestone now always require confirmed:true before
creation (structural entities — must be intentional). Both are also guarded
by exact + fuzzy title checks post-confirmation.
create_note and create_task gain an optional confirmed parameter (not
required by default; only used to bypass a content-similarity soft-block
after the user has been asked).
Helpers added to tools.py:
- _fuzzy_title_match(title, candidates, threshold=0.82)
- _PUNCT_RE for stripping punctuation before word-search candidate fetch
- difflib.SequenceMatcher and re imported at module level
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- routes/tasks.py: POST + PUT were silently dropping project_id,
milestone_id, parent_id from request body — root cause of association
not saving from the task editor
- routes/tasks.py: GET /api/tasks/:id now includes parent_title in
response (secondary lookup when parent_id is set)
- routes/notes.py: add PATCH /api/notes/:id for partial updates (used
by sub-task status toggle; PUT already existed but PATCH was missing)
- routes/projects.py: GET /api/projects/:id/notes now fetches milestone
IDs and passes them via milestone_ids so tasks assigned to a milestone
(but lacking project_id) are included in the project view
- services/notes.py: create_note() auto-sets project_id from milestone
when milestone_id is provided and project_id is omitted; list_notes()
gains milestone_ids param — when combined with project_id uses OR
condition (project_id=X OR milestone_id IN (...))
Frontend:
- NoteEditorView: add MilestoneSelector; milestone resets when project
changes; all save paths (save/create/auto-save) include milestone_id
- stores/notes.ts: add milestone_id to createNote + updateNote types
- TaskEditorView: sub-tasks now inherit milestone_id from parent task
- AppHeader: three-zone layout — brand left, Notes/Projects/Tasks/Chat
centered (absolute positioning), right rail with status/theme/? and
gear dropdown containing Settings/Users/Logs; mobile dropdown unchanged
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>