Implement tasks

This commit is contained in:
2026-03-02 12:45:24 +00:00
parent 6986c1b5ab
commit e1de6d016d
29 changed files with 2970 additions and 172 deletions

View File

@@ -52,6 +52,14 @@
<li><strong>action post_result</strong>: fan out generated result to enabled egress bindings.</li>
<li><strong>position</strong>: execution order (lower runs first).</li>
</ul>
{% if profile.slug == "bp" %}
<p><strong>Supported Triggers (BP)</strong></p>
<ul>
<li><code>#bp#</code>: primary BP trigger (uses the standard BP extraction flow).</li>
<li><code>#bp set#</code>: deterministic no-AI set/update from reply/addendum text.</li>
<li><code>#bp set range#</code>: deterministic no-AI set/update from reply-anchor to trigger range.</li>
</ul>
{% endif %}
</div>
<form method="post" style="margin-bottom: 0.75rem;" aria-label="Update command profile {{ profile.name }}">
{% csrf_token %}

View File

@@ -0,0 +1,34 @@
{% extends "base.html" %}
{% block content %}
<section class="section"><div class="container">
<h1 class="title is-4">Task #{{ task.reference_code }}: {{ task.title }}</h1>
<p class="subtitle is-6">{{ task.project.name }}{% if task.epic %} / {{ task.epic.name }}{% endif %} · {{ task.status_snapshot }}</p>
<div class="buttons"><a class="button is-small is-light" href="{% url 'tasks_hub' %}">Back</a></div>
<article class="box">
<h2 class="title is-6">Events</h2>
<table class="table is-fullwidth is-size-7">
<thead><tr><th>When</th><th>Type</th><th>Actor</th><th>Payload</th></tr></thead>
<tbody>
{% for row in events %}
<tr><td>{{ row.created_at }}</td><td>{{ row.event_type }}</td><td>{{ row.actor_identifier }}</td><td><code>{{ row.payload }}</code></td></tr>
{% empty %}
<tr><td colspan="4">No events.</td></tr>
{% endfor %}
</tbody>
</table>
</article>
<article class="box">
<h2 class="title is-6">External Sync</h2>
<table class="table is-fullwidth is-size-7">
<thead><tr><th>When</th><th>Provider</th><th>Status</th><th>Error</th></tr></thead>
<tbody>
{% for row in sync_events %}
<tr><td>{{ row.updated_at }}</td><td>{{ row.provider }}</td><td>{{ row.status }}</td><td>{{ row.error }}</td></tr>
{% empty %}
<tr><td colspan="4">No sync events.</td></tr>
{% endfor %}
</tbody>
</table>
</article>
</div></section>
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block content %}
<section class="section"><div class="container">
<h1 class="title is-4">Epic: {{ epic.name }}</h1>
<div class="buttons"><a class="button is-small is-light" href="{% url 'tasks_project' project_id=epic.project_id %}">Back to project</a></div>
<article class="box">
<ul>
{% for row in tasks %}
<li><a href="{% url 'tasks_task' task_id=row.id %}">#{{ row.reference_code }} {{ row.title }}</a></li>
{% empty %}
<li>No tasks.</li>
{% endfor %}
</ul>
</article>
</div></section>
{% endblock %}

View File

@@ -0,0 +1,41 @@
{% extends "base.html" %}
{% block content %}
<section class="section"><div class="container">
<h1 class="title is-4">Group Tasks: {{ channel_display_name }}</h1>
<p class="subtitle is-6">{{ service_label }} · {{ identifier }}</p>
{% if not tasks %}
<article class="box">
<h2 class="title is-6">No Tasks Yet</h2>
<div class="content is-size-7">
<p>This group has no derived tasks yet. To start populating this view:</p>
<ol>
<li>Open <a href="{% url 'tasks_settings' %}?service={{ service }}&identifier={{ identifier|urlencode }}">Task Settings</a> and confirm this chat is mapped under <strong>Group Mapping</strong>.</li>
<li>Send task-like messages in this group, for example: <code>task: ship v1</code>, <code>todo: write tests</code>, <code>please review PR</code>.</li>
<li>Mark completion explicitly with a phrase + reference, for example: <code>done #12</code>, <code>completed #12</code>, <code>fixed #12</code>.</li>
<li>Refresh this page; new derived tasks and events should appear automatically.</li>
</ol>
</div>
</article>
{% endif %}
<article class="box">
<h2 class="title is-6">Mappings</h2>
<ul>
{% for row in mappings %}
<li>{{ row.project.name }}{% if row.epic %} / {{ row.epic.name }}{% endif %}</li>
{% empty %}
<li>No mappings for this group.</li>
{% endfor %}
</ul>
</article>
<article class="box">
<h2 class="title is-6">Derived Tasks</h2>
<ul>
{% for row in tasks %}
<li><a href="{% url 'tasks_task' task_id=row.id %}">#{{ row.reference_code }} {{ row.title }}</a></li>
{% empty %}
<li>No tasks yet.</li>
{% endfor %}
</ul>
</article>
</div></section>
{% endblock %}

View File

@@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block content %}
<section class="section">
<div class="container">
<h1 class="title is-4">Tasks</h1>
<p class="subtitle is-6">Immutable tasks derived from chat activity.</p>
<div class="buttons">
<a class="button is-small is-link is-light" href="{% url 'tasks_settings' %}">Task Settings</a>
</div>
<div class="columns">
<div class="column is-4">
<article class="box">
<h2 class="title is-6">Projects</h2>
<ul>
{% for project in projects %}
<li><a href="{% url 'tasks_project' project_id=project.id %}">{{ project.name }}</a> <span class="has-text-grey">({{ project.task_count }})</span></li>
{% empty %}
<li>No projects yet.</li>
{% endfor %}
</ul>
</article>
</div>
<div class="column">
<article class="box">
<h2 class="title is-6">Recent Derived Tasks</h2>
<table class="table is-fullwidth is-striped is-size-7">
<thead><tr><th>Ref</th><th>Title</th><th>Project</th><th>Status</th><th></th></tr></thead>
<tbody>
{% for row in tasks %}
<tr>
<td>#{{ row.reference_code }}</td>
<td>{{ row.title }}</td>
<td>{{ row.project.name }}{% if row.epic %} / {{ row.epic.name }}{% endif %}</td>
<td>{{ row.status_snapshot }}</td>
<td><a class="button is-small is-light" href="{% url 'tasks_task' task_id=row.id %}">Open</a></td>
</tr>
{% empty %}
<tr><td colspan="5">No derived tasks yet.</td></tr>
{% endfor %}
</tbody>
</table>
</article>
</div>
</div>
</div>
</section>
{% endblock %}

View File

@@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block content %}
<section class="section"><div class="container">
<h1 class="title is-4">Project: {{ project.name }}</h1>
<div class="buttons"><a class="button is-small is-light" href="{% url 'tasks_hub' %}">Back</a></div>
<article class="box">
<h2 class="title is-6">Epics</h2>
<ul>
{% for epic in epics %}
<li><a href="{% url 'tasks_epic' epic_id=epic.id %}">{{ epic.name }}</a></li>
{% empty %}
<li>No epics.</li>
{% endfor %}
</ul>
</article>
<article class="box">
<h2 class="title is-6">Tasks</h2>
<ul>
{% for row in tasks %}
<li><a href="{% url 'tasks_task' task_id=row.id %}">#{{ row.reference_code }} {{ row.title }}</a></li>
{% empty %}
<li>No tasks.</li>
{% endfor %}
</ul>
</article>
</div></section>
{% endblock %}

View File

@@ -0,0 +1,386 @@
{% extends "base.html" %}
{% block content %}
<section class="section">
<div class="container tasks-settings-page">
<h1 class="title is-4">Task Settings</h1>
<p class="subtitle is-6">Configure task derivation, chat mapping, completion parsing, and external sync behavior.</p>
<article class="box">
<h2 class="title is-6">Setting Definitions</h2>
<div class="content is-size-7">
<p><strong>Projects</strong>: top-level containers for derived tasks. A single group can map to any project.</p>
<p><strong>Epics</strong>: optional sub-grouping inside a project. Use these for parallel workstreams in the same project.</p>
<p><strong>Group Mapping</strong>: binds a chat channel (service + channel identifier) to a project and optional epic. Task extraction only runs where mappings exist.</p>
<p><strong>Matching Hierarchy</strong>: channel mapping flags override project flags. Project flags are defaults; mapping flags are per-chat precision controls.</p>
<p><strong>False-Positive Controls</strong>: defaults are safe: <code>match_mode=strict</code>, <code>require_prefix=true</code>, and prefixes <code>task:</code>/<code>todo:</code>. Freeform matching is off by default.</p>
<p><strong>Task ID Announcements</strong>: when enabled, newly derived tasks post an in-chat confirmation containing the new task reference (for example <code>#17</code>).</p>
<p><strong>Completion Phrases</strong>: explicit trigger words used to detect completion markers like <code>done #12</code>, <code>completed #12</code>, <code>fixed #12</code>.</p>
<p><strong>Provider</strong>: external sync adapter toggle. In current setup, mock provider validates append-only sync flow and retry behavior.</p>
<p><strong>Sync Event Log</strong>: audit of provider sync attempts and outcomes. Retry replays the event without mutating immutable task source records.</p>
</div>
</article>
{% if prefill_service and prefill_identifier %}
<article class="box">
<h2 class="title is-6">Quick Setup For Current Chat</h2>
<p class="help">Prefilled from compose for <code>{{ prefill_service }}</code> · <code>{{ prefill_identifier }}</code>. Create/update project + epic + channel mapping in one step.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="quick_setup">
<input type="hidden" name="service" value="{{ prefill_service }}">
<input type="hidden" name="channel_identifier" value="{{ prefill_identifier }}">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<div class="columns">
<div class="column">
<label class="label is-size-7">Project</label>
<input class="input is-small" name="project_name" placeholder="Project name">
</div>
<div class="column">
<label class="label is-size-7">Epic (optional)</label>
<input class="input is-small" name="epic_name" placeholder="Epic name">
</div>
<div class="column">
<label class="label is-size-7">Match Mode</label>
<div class="select is-small is-fullwidth">
<select name="source_match_mode">
<option value="strict">strict</option>
<option value="balanced">balanced</option>
<option value="broad">broad</option>
</select>
</div>
</div>
<div class="column">
<label class="label is-size-7">Prefixes</label>
<input class="input is-small" name="source_allowed_prefixes" value="task:,todo:">
</div>
</div>
<label class="checkbox is-size-7"><input type="checkbox" name="source_require_prefix" value="1" checked> Require Prefix</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_completion_enabled" value="1" checked> Completion Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_derive_enabled" value="1" checked> Derivation Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_announce_task_id" value="1" checked> Announce Task ID</label>
<button class="button is-small is-link" type="submit" style="margin-left: 0.75rem;">Apply Quick Setup</button>
</form>
</article>
{% endif %}
<div class="columns is-multiline tasks-settings-grid">
<div class="column is-6">
<article class="box">
<h2 class="title is-6">Projects</h2>
<p class="help">Create project scopes used by group mappings and derived tasks.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="project_create">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<div class="field">
<label class="label is-size-7">Name</label>
<input class="input is-small" name="name" placeholder="Project name">
</div>
<div class="field">
<label class="label is-size-7">Match Mode</label>
<div class="select is-small is-fullwidth">
<select name="match_mode">
<option value="strict">strict</option>
<option value="balanced">balanced</option>
<option value="broad">broad</option>
</select>
</div>
</div>
<div class="field">
<label class="label is-size-7">Allowed Prefixes (comma-separated)</label>
<input class="input is-small" name="allowed_prefixes" value="task:,todo:">
</div>
<label class="checkbox is-size-7"><input type="checkbox" name="require_prefix" value="1" checked> Require Prefix</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="derive_enabled" value="1" checked> Derivation Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="completion_enabled" value="1" checked> Completion Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="announce_task_id" value="1" checked> Announce Task ID</label>
<button class="button is-small is-link" type="submit">Add Project</button>
</form>
<ul class="tasks-settings-list">
{% for row in projects %}<li>{{ row.name }}</li>{% empty %}<li>No projects.</li>{% endfor %}
</ul>
</article>
</div>
<div class="column is-6">
<article class="box">
<h2 class="title is-6">Epics</h2>
<p class="help">Create project-local epics to refine routing and reporting.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="epic_create">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<div class="field">
<label class="label is-size-7">Project</label>
<div class="select is-small is-fullwidth">
<select name="project_id">
{% for p in projects %}<option value="{{ p.id }}">{{ p.name }}</option>{% endfor %}
</select>
</div>
</div>
<div class="field">
<label class="label is-size-7">Name</label>
<input class="input is-small" name="name" placeholder="Epic name">
</div>
<button class="button is-small is-link" type="submit">Add Epic</button>
</form>
</article>
</div>
<div class="column is-12">
<article class="box">
<h2 class="title is-6">Group Mapping (Chat -> Project/Epic)</h2>
<p class="help">Each mapped group becomes eligible for derived task extraction and completion tracking.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="source_create">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<div class="columns">
<div class="column">
<label class="label is-size-7">Service</label>
<div class="select is-small is-fullwidth">
<select name="service">
<option {% if prefill_service == 'web' %}selected{% endif %}>web</option>
<option {% if prefill_service == 'xmpp' %}selected{% endif %}>xmpp</option>
<option {% if prefill_service == 'signal' %}selected{% endif %}>signal</option>
<option {% if prefill_service == 'whatsapp' %}selected{% endif %}>whatsapp</option>
</select>
</div>
</div>
<div class="column">
<label class="label is-size-7">Channel Identifier</label>
<input class="input is-small" name="channel_identifier" placeholder="service-native group/channel id" value="{{ prefill_identifier }}">
</div>
<div class="column">
<label class="label is-size-7">Project</label>
<div class="select is-small is-fullwidth">
<select name="project_id">
{% for p in projects %}<option value="{{ p.id }}">{{ p.name }}</option>{% endfor %}
</select>
</div>
</div>
<div class="column">
<label class="label is-size-7">Epic (optional)</label>
<div class="select is-small is-fullwidth">
<select name="epic_id">
<option value="">-</option>
{% for e in epics %}<option value="{{ e.id }}">{{ e.project.name }} / {{ e.name }}</option>{% endfor %}
</select>
</div>
</div>
<div class="column is-narrow">
<button class="button is-small is-link" type="submit" style="margin-top: 1.8rem;">Add</button>
</div>
</div>
<div class="columns">
<div class="column">
<label class="label is-size-7">Match Mode</label>
<div class="select is-small is-fullwidth">
<select name="source_match_mode">
<option value="strict">strict</option>
<option value="balanced">balanced</option>
<option value="broad">broad</option>
</select>
</div>
</div>
<div class="column">
<label class="label is-size-7">Allowed Prefixes</label>
<input class="input is-small" name="source_allowed_prefixes" value="task:,todo:">
</div>
<div class="column">
<label class="label is-size-7">Min Chars</label>
<input class="input is-small" name="source_min_chars" value="3">
</div>
</div>
<label class="checkbox is-size-7"><input type="checkbox" name="source_require_prefix" value="1" checked> Require Prefix</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_derive_enabled" value="1" checked> Derivation Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_completion_enabled" value="1" checked> Completion Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_ai_title_enabled" value="1" checked> AI Title Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_announce_task_id" value="1" checked> Announce Task ID</label>
</form>
<table class="table is-fullwidth is-size-7">
<thead><tr><th>Chat</th><th>Project</th><th>Epic</th></tr></thead>
<tbody>
{% for row in sources %}
<tr><td>{{ row.service }} · {{ row.channel_identifier }}</td><td>{{ row.project.name }}</td><td>{{ row.epic.name }}</td></tr>
{% empty %}
<tr><td colspan="3">No mappings.</td></tr>
{% endfor %}
</tbody>
</table>
</article>
</div>
<div class="column is-6">
<article class="box">
<h2 class="title is-6">Project Matching Flags</h2>
<p class="help">Project defaults apply to all mapped chats unless channel-level override changes them.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="project_flags_update">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<div class="field">
<label class="label is-size-7">Project</label>
<div class="select is-small is-fullwidth">
<select name="project_id">
{% for p in projects %}
<option value="{{ p.id }}">{{ p.name }} · mode={{ p.settings_effective.match_mode }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="field">
<label class="label is-size-7">Match Mode</label>
<div class="select is-small is-fullwidth">
<select name="match_mode">
<option value="strict">strict</option>
<option value="balanced">balanced</option>
<option value="broad">broad</option>
</select>
</div>
</div>
<div class="field">
<label class="label is-size-7">Allowed Prefixes</label>
<input class="input is-small" name="allowed_prefixes" value="task:,todo:">
</div>
<div class="field">
<label class="label is-size-7">Min Chars</label>
<input class="input is-small" name="min_chars" value="3">
</div>
<label class="checkbox is-size-7"><input type="checkbox" name="require_prefix" value="1" checked> Require Prefix</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="derive_enabled" value="1" checked> Derivation Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="completion_enabled" value="1" checked> Completion Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="announce_task_id" value="1" checked> Announce Task ID</label>
<button class="button is-small is-link is-light" type="submit" style="margin-left: 0.75rem;">Save Project Flags</button>
</form>
</article>
<article class="box">
<h2 class="title is-6">Channel Override Flags</h2>
<p class="help">These flags override project defaults for one mapped chat only.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="source_flags_update">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<div class="field">
<label class="label is-size-7">Mapped Channel</label>
<div class="select is-small is-fullwidth">
<select name="source_id">
{% for s in sources %}
<option value="{{ s.id }}">{{ s.service }} · {{ s.channel_identifier }} · {{ s.project.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="field">
<label class="label is-size-7">Match Mode</label>
<div class="select is-small is-fullwidth">
<select name="source_match_mode">
<option value="strict">strict</option>
<option value="balanced">balanced</option>
<option value="broad">broad</option>
</select>
</div>
</div>
<div class="field">
<label class="label is-size-7">Allowed Prefixes</label>
<input class="input is-small" name="source_allowed_prefixes" value="task:,todo:">
</div>
<div class="field">
<label class="label is-size-7">Min Chars</label>
<input class="input is-small" name="source_min_chars" value="3">
</div>
<label class="checkbox is-size-7"><input type="checkbox" name="source_require_prefix" value="1" checked> Require Prefix</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_derive_enabled" value="1" checked> Derivation Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_completion_enabled" value="1" checked> Completion Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_ai_title_enabled" value="1" checked> AI Title Enabled</label>
<label class="checkbox is-size-7" style="margin-left: 0.75rem;"><input type="checkbox" name="source_announce_task_id" value="1" checked> Announce Task ID</label>
<button class="button is-small is-link is-light" type="submit" style="margin-left: 0.75rem;">Save Channel Flags</button>
</form>
</article>
</div>
<div class="column is-6">
<article class="box">
<h2 class="title is-6">Completion Phrases</h2>
<p class="help">Add parser phrases for completion statements followed by a task reference, e.g. <code>done #12</code>.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="pattern_create">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<div class="field">
<label class="label is-size-7">Phrase</label>
<input class="input is-small" name="phrase" placeholder="done">
</div>
<button class="button is-small is-link" type="submit">Add Phrase</button>
</form>
<ul class="tasks-settings-list">
{% for row in patterns %}<li>{{ row.phrase }}</li>{% empty %}<li>No phrases.</li>{% endfor %}
</ul>
</article>
</div>
<div class="column is-6">
<article class="box">
<h2 class="title is-6">Provider</h2>
<p class="help">Enable/disable external sync adapter and review recent provider event outcomes.</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="provider_update">
<input type="hidden" name="provider" value="mock">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<label class="checkbox"><input type="checkbox" name="enabled" value="1" {% if provider_configs and provider_configs.0.enabled %}checked{% endif %}> Enable mock provider</label>
<button class="button is-small is-link is-light" type="submit">Save</button>
</form>
<table class="table is-fullwidth is-size-7 tasks-settings-table">
<thead><tr><th>Updated</th><th>Provider</th><th>Status</th><th></th></tr></thead>
<tbody>
{% for row in sync_events %}
<tr>
<td>{{ row.updated_at }}</td>
<td>{{ row.provider }}</td>
<td>{{ row.status }}</td>
<td>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="sync_retry">
<input type="hidden" name="event_id" value="{{ row.id }}">
<input type="hidden" name="prefill_service" value="{{ prefill_service }}">
<input type="hidden" name="prefill_identifier" value="{{ prefill_identifier }}">
<button class="button is-small is-light" type="submit">Retry</button>
</form>
</td>
</tr>
{% empty %}
<tr><td colspan="4">No sync events.</td></tr>
{% endfor %}
</tbody>
</table>
</article>
</div>
</div>
</div>
</section>
<style>
.tasks-settings-page .tasks-settings-grid .column > .box {
height: 100%;
display: flex;
flex-direction: column;
}
.tasks-settings-page .tasks-settings-list {
margin-top: 0.75rem;
}
.tasks-settings-page .tasks-settings-table {
margin-top: 0.75rem;
}
</style>
{% endblock %}