Commit Graph

57 Commits

Author SHA1 Message Date
345e0f3a04 feat(schedule_type): minute-precision windows + variable maintenance length
Lifts the two hard restrictions in PR #18:
  * window bounds were `int hour` (0-23) → now `int minutes-since-UTC-midnight` (0-1439)
  * maintenance window was exactly 1 hour → now any duration in [1, 180] minutes
    ((maint_to - maint_from) mod 1440)

## Schema migration (additive)

`_migrate_schema()` detects legacy "hours" rows (any row where MAX of the
6 window columns is ≤ 23) and multiplies each column by 60 to convert
to minutes. Idempotent — post-conversion values are well above 23 so
the guard never fires twice.

## Touched surfaces

- `models/schedule_type.py` — column comments updated; new
  `compute_maintenance_duration()` helper (returns 1-1440 min, treats
  from==to as 1440 which is then rejected by validator)
- `schemas/schedule_type.py` — `*_from`/`*_to` upper bound 23 → 1440;
  `_validate_maintenance_window` accepts 1-180min duration; response
  includes derived `maintenance_duration_minutes`
- `schemas/schedule_type_special_slot.py` — `minute_in_window` max
  59→179; `estimated_duration` max 60→180
- `routers/schedule_type.py` — PATCH re-validates merged maintenance
  pair (partial updates can put the row into an invalid combo the
  pydantic single-field validator can't catch); `_attach_derived`
  populates the new response field
- `routers/schedule_type_special_slot.py` — `_validate_fits_window`
  now takes the parent's maintenance duration instead of hard-coded 60
- `routers/calendar.py` — `_scheduled_inside_window` arg renamed
  hour→min; the maintenance-window guard error message formats
  HH:MM not HH:00
- `services/special_slot_materialiser.py` — materialised
  `scheduled_at` derived from `(maint_from_min + tpl.minute_in_window)`
  with hour/minute split

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 20:18:21 +01:00
dcaaa4259a feat(calendar): maintenance window + schedule_type special slots
## What this adds

1. **Maintenance window on ScheduleType**
   - New columns: maintenance_from / maintenance_to (UTC hours, 0-23)
   - Invariant: window is exactly 1 hour (validated in pydantic;
     maintenance_to must equal (maintenance_from + 1) % 24)
   - Default applied via additive migration: 8:00-9:00 UTC for existing
     rows so deployments don't crash on first boot

2. **ScheduleTypeSpecialSlot** — admin-managed slot template
   - New table schedule_type_special_slots
   - Admin (schedule_type.manage) CRUD via
     /schedule-types/{id}/special-slots
   - Fields: name, description, minute_in_window (0-59 inside the
     parent maintenance window), estimated_duration, priority,
     event_data (JSON merged into materialised slot), is_active
   - Unique constraint (schedule_type_id, name) — name is the stable
     human-readable identifier per cohort

3. **Per-agent materialisation**
   - New service app/services/special_slot_materialiser.py
   - GET /calendar/sync calls materialise_special_slots_for_claw
     (idempotent, one row per agent per template per date)
   - GET /calendar/day calls materialise_special_slots_for_user
   - Materialised rows are slot_type=system, event_type=system_event,
     is_admin_locked=true, special_slot_id pointing back to template
   - Plugin's runSync picks them up like any other due slot via the
     normal real-slots query path

4. **Admin-locked enforcement**
   - New TimeSlot columns: is_admin_locked, special_slot_id (FK to
     schedule_type_special_slots, ON DELETE SET NULL)
   - PATCH /calendar/slots/{id}: refuses any edit on admin-locked
     slots (423)
   - POST /calendar/slots/{id}/cancel: refuses cancel on admin-locked
     (423)
   - PATCH /calendar/slots/{id}/agent-update: admin-locked accept only
     ongoing/paused/finished/aborted statuses (423 on other transitions)

5. **Maintenance-window guard on slot creation**
   - POST /calendar/slots: rejects slot_type=system outright (only
     materialiser may create system slots) and rejects any non-system
     slot whose [scheduled_at, +duration] intersects the calling
     user's schedule_type maintenance window (422). Handles 23->0 wrap

6. **Schema response**
   - TimeSlotResponse / CalendarSlotItem now include is_admin_locked
     and special_slot_id so clients can render the lock indicator and
     trace back to the template

## Migration

Additive only — no destructive changes. Lives in _migrate_schema()
in app/main.py; the new schedule_type_special_slots table is created
by Base.metadata.create_all() on first boot.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 19:18:42 +01:00
1c91cb32fc feat(auth): OIDC-only admin-role bootstrap auto-connect
In OIDC-only mode, before any admin is linked, an IdP user whose token
carries the configured admin role (default "admin"; OIDC_ADMIN_ROLE /
oidc_settings.admin_role) auto-connects to the unbound hf admin on
first OIDC sign-in, then the window self-closes once any admin is
bound. Roles are scanned across userinfo + the (unverified) access
token: realm_access.roles, resource_access.*.roles, roles/role/groups.
Adds admin_role to settings model/env/effective/API and to the wizard
bootstrap config. Replaces the manual admin-subject approach.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 21:05:39 +01:00
f8126d0cbc feat(auth): admin-configurable OIDC provider (oidc_settings)
Persist OIDC config in a single-row oidc_settings table; non-empty DB
fields override the OIDC_* env vars (env = bootstrap default). The
Authlib client is rebuilt when config changes.

- GET/PUT /auth/oidc/settings — admin only, via JWT OR API key. The
  API-key path is the recovery channel when OIDC-only mode is on and
  OIDC is misconfigured (avoids total lockout).
- client_secret is write-only: never returned (has_client_secret bool),
  preserved when the field is left blank on update.
- /auth/config, login/link/callback now use the effective (DB|env)
  config so enabling OIDC needs no redeploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 20:29:15 +01:00
54b6103880 feat(auth): OIDC login + identity binding + HARBORFORGE_OIDC_ONLY
- Generic OIDC (Authlib discovery) Authorization Code flow; backend
  issues the existing HS256 JWT on success. Unbound identities are
  rejected (no auto-provisioning).
- User.oidc_issuer/oidc_subject (unique together) + startup migration.
- PUT/DELETE /users/{id}/oidc-binding (admin or account-manager;
  JWT or API key; 409 on conflict). Self-link /auth/oidc/link
  (non-OIDC_ONLY only). Public GET /auth/config.
- HARBORFORGE_OIDC_ONLY: /auth/token rejected, create/update ignore
  password (passwordless users; API keys + OIDC still work).
- Dockerfile ARG/ENV HARBORFORGE_OIDC_ONLY; authlib+itsdangerous deps;
  SessionMiddleware for OIDC state. Fixed _user_response to expose
  the new binding fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 20:22:04 +01:00
zhi
8ab9cae474 feat: schedule type system for work/entertainment periods
- New model: ScheduleType (name, work_from/to, entertainment_from/to)
- Agent.schedule_type_id FK to schedule_types
- CRUD API: GET/POST/PATCH/DELETE /schedule-types/
- Agent assignment: PUT /schedule-types/agent/{agent_id}/assign
- Agent self-query: GET /schedule-types/agent/me
- Permissions: schedule_type.read, schedule_type.manage
- Migration: adds schedule_type_id column to agents table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:20:51 +00:00
755c418391 feat: auto-trigger Discord wakeup when slot becomes ONGOING 2026-04-05 09:37:14 +00:00
79c6c32a78 feat: store discord user ids on accounts 2026-04-04 20:16:22 +00:00
0448cde765 fix: make code index migration mysql-compatible 2026-04-03 19:00:45 +00:00
ae353afbed feat: switch backend indexing to code-first identifiers 2026-04-03 16:25:11 +00:00
zhi
eb57197020 BE-CAL-004: implement MinimumWorkload storage
- New model: minimum_workloads table with JSON config column (per-user)
- Schemas: MinimumWorkloadConfig, MinimumWorkloadUpdate, MinimumWorkloadResponse
- Service: CRUD operations + check_workload_warnings() entry point for BE-CAL-007
- API: GET/PUT/PATCH /calendar/workload-config (self + admin routes)
- Migration: auto-create minimum_workloads table on startup
- Registered calendar router in main.py
2026-03-30 22:27:05 +00:00
zhi
1c062ff4f1 BE-CAL-003: Add Agent model with status/heartbeat/exhausted fields
- New app/models/agent.py with Agent model, AgentStatus & ExhaustReason enums
- Agent has 1-to-1 FK to User, unique agent_id (OpenClaw $AGENT_ID),
  claw_identifier (OpenClaw instance, convention-matches MonitoredServer.identifier)
- Status fields: status (idle/on_call/busy/exhausted/offline), last_heartbeat
- Exhausted tracking: exhausted_at, recovery_at, exhaust_reason (rate_limit/billing)
- User model: added 'agent' back-reference (uselist=False)
- Schemas: AgentResponse, AgentStatusUpdate, UserCreate now accepts agent_id+claw_identifier
- UserResponse: includes agent_id when agent is bound
- Users router: create_user creates Agent record when agent_id+claw_identifier provided
- Auto-migration: CREATE TABLE agents in _migrate_schema()
- Startup imports: agent and calendar models registered
2026-03-30 20:47:44 +00:00
zhi
431f4abe5a BE-PR-006: Add Essential CRUD API under Proposals
- New router: /projects/{project_id}/proposals/{proposal_id}/essentials
  - GET (list), POST (create), GET/{id}, PATCH/{id}, DELETE/{id}
- All mutations restricted to open proposals only
- Permission: creator, project owner, or global admin
- Registered essentials router in main.py
- Updated GET /proposals/{id} to return ProposalDetailResponse with
  embedded essentials list
- Activity logging on all CRUD operations
2026-03-30 07:16:30 +00:00
zhi
089d75f953 BE-PR-003: Add Essential SQLAlchemy model
- New app/models/essential.py with Essential model and EssentialType enum
  (feature, improvement, refactor)
- Fields: id, essential_code (unique), proposal_id (FK to proposes),
  type, title, description, created_by_id (FK to users), created_at, updated_at
- Added essentials relationship to Proposal model (cascade delete-orphan)
- Added essentials table auto-migration in main.py _migrate_schema()
- Registered essential module import in startup()
2026-03-29 16:33:00 +00:00
zhi
cfacd432f5 BE-PR-001: Rename Propose -> Proposal across backend
- New canonical model: Proposal, ProposalStatus (app/models/proposal.py)
- New canonical router: /projects/{id}/proposals (app/api/routers/proposals.py)
- Schemas renamed: ProposalCreate, ProposalUpdate, ProposalResponse, etc.
- Old propose.py and proposes.py kept as backward-compat shims
- Legacy /proposes API still works (delegates to /proposals handlers)
- DB table name (proposes), column (propose_code), and permission names
  (propose.*) kept unchanged for zero-migration compat
- Updated init_wizard.py comments
2026-03-29 15:35:23 +00:00
zhi
1905378064 Merge fix/three-bugs-2026-03-22: accept task_code/milestone_code as identifiers, add /config/status endpoint 2026-03-22 10:56:34 +00:00
zhi
8b357aabc4 Fix: accept task_code/milestone_code as identifiers, add /config/status endpoint
- All /tasks/{task_id} endpoints now accept both numeric id and task_code string
- All /milestones/{milestone_id} endpoints (misc.py) now accept both numeric id and milestone_code
- Added _resolve_task() and _resolve_milestone() helpers
- GET /config/status reads initialization state from config volume (no wizard dependency)
- MilestoneResponse schema now includes milestone_code field
- Comments and worklog endpoints also accept task_code
2026-03-22 10:06:27 +00:00
zhi
86911286c0 feat: add code-based meetings router with participant/attend support
- New dedicated meetings.py router with full CRUD (list/get/create/update/delete)
- All endpoints accept meeting_code or numeric id
- MeetingParticipant model for tracking meeting attendance
- POST /meetings/{id}/attend adds current user to participant list
- Serialization includes participants list, project_code, milestone_code
- Creator auto-added as participant on meeting creation
- Registered in main.py alongside existing routers
2026-03-21 19:18:20 +00:00
zhi
271d5140e6 feat(users): switch account management to single-role model
- add users.role_id for one global role per account
- seed protected account-manager role with account.create permission
- default new accounts to guest role
- block admin role assignment through user management
- allow account-manager permission to create accounts
2026-03-21 08:44:19 +00:00
zhi
14dcda3cdc feat(monitor): store nginx telemetry for generic clients
- accept nginx installation status and sites-enabled list
- persist nginx fields in server state
- expose nginx data in monitor overview/admin views
- auto-migrate new server_states columns on startup
2026-03-20 10:03:56 +00:00
zhi
97f12cac7a feat(monitor): store plugin version separately from openclaw version
- Add server_states.plugin_version column
- Keep openclaw_version for remote OpenClaw runtime version
- Expose plugin_version in monitor server view
- Accept and persist plugin_version in heartbeat payloads
2026-03-20 07:23:18 +00:00
zhi
c70f90cb52 feat(monitor): add API Key authentication for server heartbeat
- Add api_key field to MonitoredServer model with unique index
- Add migration to create api_key column
- Add POST /admin/servers/{id}/api-key for key generation
- Add DELETE /admin/servers/{id}/api-key for key revocation
- Add POST /server/heartbeat-v2 with X-API-Key header auth
- TelemetryPayload includes load_avg and uptime_seconds
2026-03-19 18:17:50 +00:00
zhi
89e3bcdd0f feat(P7.1): remove task_type='task' — migrate to issue/defect, update defaults and DB migration 2026-03-17 16:05:32 +00:00
zhi
7d8c448cb8 feat(P3.1): milestone action endpoints — freeze/start/close + auto-complete hook
- New milestone_actions router with POST freeze/start/close endpoints
- freeze: validates exactly 1 release maintenance task exists
- start: validates all milestone/task dependencies completed, records started_at
- close: allows from open/freeze/undergoing with reason
- try_auto_complete_milestone helper: auto-completes milestone when sole release task finishes
- Wired auto-complete into task transition and update endpoints
- Added freeze enforcement: no new feature story tasks after freeze
- Added started_at to milestone serializer
- All actions write activity logs
2026-03-17 04:03:05 +00:00
zhi
75ccbcb362 feat: propose CRUD router + accept/reject/reopen actions (P6.1-P6.4) 2026-03-17 03:01:49 +00:00
zhi
2bea75e843 feat: add Propose model/schema + DB enum migration scripts
- New Propose model (app/models/propose.py) with status enum (open/accepted/rejected)
- New Propose schemas (ProposeCreate/Update/Response) in schemas.py
- MySQL enum migration in main.py for milestone/task status columns
  - milestone: pending→open, deferred→closed, progressing→undergoing
  - task: progressing→undergoing
- Import propose model in startup for create_all
- Add started_at column migration for milestones
2026-03-17 02:04:42 +00:00
zhi
9e14df921e feat: add modal-edit permissions for projects milestones and tasks 2026-03-16 18:13:54 +00:00
zhi
214a9b109d refactor: replace issues backend with milestone tasks 2026-03-16 13:22:14 +00:00
Zhi
74177915df feat: add configurable role/permission system 2026-03-12 11:41:55 +00:00
Zhi
2f659e1430 feat: add project creation permission (admin only), add milestones API with RBAC 2026-03-12 11:04:04 +00:00
Zhi
1eb90cd61c fix: project create schema - owner_name auto-fill from owner_id, sub/related projects as list 2026-03-12 10:52:46 +00:00
zhi
e5775bb9c8 feat: add project code generation + remove issues/milestones from nav 2026-03-12 09:25:26 +00:00
zhi
6b3e42195d feat: add task type hierarchy with subtypes (issue/meeting/support/maintenance/research/review/story/test) 2026-03-11 23:55:52 +00:00
zhi
d299428d35 feat: add public monitor API + admin provider/server management scaffold 2026-03-11 11:59:53 +00:00
zhi
4b20444a5e feat: AbstractWizard integration for initialization
- Add init_wizard.py: fetch config from AbstractWizard on startup
- Create admin user if not exists (from wizard config)
- Create default project if configured
- Graceful fallback when wizard is unavailable
2026-03-06 13:15:47 +00:00
Zhi
f60dc68b22 refactor: split monolithic main.py into FastAPI routers (v0.2.0)
- app/api/deps.py: shared auth dependencies
- app/api/routers/auth.py: login, me
- app/api/routers/issues.py: CRUD, transition, assign, relations, tags, batch, search
- app/api/routers/projects.py: CRUD, members, worklog summary
- app/api/routers/users.py: CRUD, worklogs
- app/api/routers/comments.py: CRUD
- app/api/routers/webhooks.py: CRUD, logs, retry
- app/api/routers/misc.py: API keys, activity, milestones, notifications, worklogs, export, dashboard
- main.py: 1165 lines → 51 lines
- Version bump to 0.2.0
2026-02-23 15:14:46 +00:00
Zhi
107102e775 feat: paginated list responses, issue sorting + filtering by assignee/tag 2026-02-23 10:12:07 +00:00
Zhi
703103af91 feat: time tracking / work logs (create, list, summary, project summary, CLI commands) 2026-02-23 05:11:52 +00:00
Zhi
0a8b18729b feat: notifications system, webhook retry, issue assign endpoint, CLI milestones/notifications/overdue commands 2026-02-23 00:11:26 +00:00
Zhi
7485f29ada feat: milestones, due dates, overdue filter, CSV export 2026-02-22 19:12:35 +00:00
Zhi
f48b829511 feat: issue tags management (add/remove/list-all) 2026-02-22 09:08:49 +00:00
Zhi
6c53a6f658 feat: issue relations (link/unlink parent-child, list children) 2026-02-22 09:08:13 +00:00
Zhi
a63afa073d feat: activity log model + API (audit trail) 2026-02-22 09:06:37 +00:00
Zhi
ac397679f8 feat: batch operations (transition + assign) 2026-02-22 09:05:42 +00:00
Zhi
1e9c6fd2f8 feat: API key auth for agents (create/list/revoke) + dual auth (JWT or API key) 2026-02-22 09:05:05 +00:00
Zhi
9d831e932c feat: comment update/delete endpoints 2026-02-22 04:22:54 +00:00
Zhi
8ac51494c4 feat: dashboard stats API (by status/type/priority) 2026-02-22 04:21:26 +00:00
Zhi
d4666260a7 feat: issue search API (keyword in title/description) 2026-02-22 04:21:01 +00:00
Zhi
9ad1e940e4 feat: issue status transition endpoint with webhook 2026-02-22 04:20:35 +00:00
Zhi
1a76de7c50 feat: webhook event firing on issue creation (background task) 2026-02-22 02:43:34 +00:00