Skip to content

Add explicit active state for accepted tasks - #715

Draft
joshsmithxrm wants to merge 4 commits into
tarkovtracker-org:mainfrom
joshsmithxrm:reeve/dispatch-tt-active-20260812-1628-a7c4
Draft

Add explicit active state for accepted tasks#715
joshsmithxrm wants to merge 4 commits into
tarkovtracker-org:mainfrom
joshsmithxrm:reeve/dispatch-tt-active-20260812-1628-a7c4

Conversation

@joshsmithxrm

@joshsmithxrm joshsmithxrm commented Aug 13, 2026

Copy link
Copy Markdown

Summary

  • add an explicit optional active state for accepted ordinary tasks
  • preserve legacy incomplete rows as unknown instead of inferring acceptance
  • persist and expose active state through the API, imports, merges, sharing, realtime, team progress, and profile views
  • keep automatically unlocked successors neutral and prevent prerequisite propagation from overwriting existing successor progress
  • distinguish Active from Available and Locked throughout task UI and public profiles

Why

The existing persisted representation uses incomplete state for both accepted tasks and tasks that are merely unlocked. That makes accepted-task state impossible to query reliably and causes log-imported task-start events to lose their meaning.

This change adds the missing lifecycle distinction without mass-migrating ambiguous legacy rows. Consumers must treat a missing active field as unknown; only active: true is authoritative acceptance.

Compatibility

  • the response field is optional for legacy records
  • existing completed, failed, and uncompleted writes remain supported and clear active state
  • auto-unlocked dependents remain explicitly neutral
  • PvE, PvP, Seasonal, shared-profile, team, merge, and realtime paths preserve the new field

Validation

  • API gateway lifecycle and dependency-propagation suites
  • focused task import/action/filter/count/graph/notification suites
  • public-profile and progress merge/sanitizer/realtime/team suites
  • Nuxt and worker typechecks
  • OpenAPI validation, ESLint, Prettier, and i18n checks

This is intentionally a draft for maintainer feedback on the additive lifecycle contract and legacy-row semantics.

Review in cubic

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ba3d7f88-b42c-4b06-b179-0669b3189704

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codeant-ai

codeant-ai Bot commented Aug 13, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 29f65bab
Scan Time: 2026-08-13 00:34:44 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@sonarqubecloud

Copy link
Copy Markdown

Comment on lines +29 to +38
const matchesActiveStatus = (completion: RawTaskCompletion): boolean =>
isTaskActive(completion) || isTaskComplete(completion);
const requiresActiveStatus = (statuses: string[]): boolean =>
['active', 'accept', 'accepted'].some((status) => statuses.includes(status));
const matchesRequiredActiveStatus = (
statuses: string[],
completion: RawTaskCompletion,
taskId: string,
isUnlockable: (taskId: string) => boolean
completion: RawTaskCompletion
): boolean => {
if (!requiresActiveStatus(statuses)) return false;
return matchesActiveStatus(completion, taskId, isUnlockable);
return matchesActiveStatus(completion);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Edge Case: Legacy accepted tasks lock downstream 'active'-requirement tasks

matchesActiveStatus in taskAvailability.ts no longer falls back to isUnlockable, and isTaskActive now returns true only when completion.active===true. Legacy accepted tasks (stored incomplete with no active field, which the PR intentionally keeps as "unknown") therefore no longer satisfy a downstream task's ['active']/accept/accepted requirement. Any task B whose taskRequirement points at such a prerequisite A will flip from available to locked for existing users who accepted A before this migration, silently hiding tasks until they re-accept. Consider treating an unknown legacy prerequisite as still satisfying an active requirement (e.g. keep an unlockable/present fallback for missing-active records) or backfilling active state for incomplete legacy rows that already gate downstream tasks.

Was this helpful? React with 👍 / 👎

Comment on lines 245 to 259
@@ -255,7 +255,7 @@ const applyStartedImports = (
for (const taskId of startedTaskIds) {
const flags = getCompletionFlags(completions[taskId]);
const shouldStart = shouldStartImportedTask(completedTaskIds.has(taskId), flags);
if (shouldStart) store.setTaskUncompleted(taskId);
if (shouldStart) store.setTaskActive(taskId);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Bug: Imported task-start restarts failed tasks, clobbering failed state

shouldStartImportedTask changed from !alreadyCompleted && !flags.complete && !flags.failed to !alreadyCompleted && (!flags.complete || flags.failed), so a task currently in the failed state (complete=true, failed=true) now returns true and applyStartedImports calls store.setTaskActive on it. Because applyStartedImports runs after applyCompletedImports, a task that was just marked failed (e.g. via a failed requirement) but also appears in startedTaskIds will have its failed state overwritten with active. If restarting failed tasks is intended, confirm the ordering doesn't erase failures derived within the same import; otherwise exclude failed tasks from the started set.

Was this helpful? React with 👍 / 👎

Comment on lines 39 to +53
@@ -46,6 +46,16 @@
>
{{ t('common.complete', 'Complete') }}
</UButton>
<UButton
v-else-if="state === 'available' && !isFailed"
:size="size"
color="primary"
variant="soft"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Available tasks can no longer be completed in one click on card

TaskCardActions now shows an 'Accept' button (emits 'active') for state==='available' and only shows 'Complete' for state==='active'. Users must now Accept then Complete, a two-step change from the previous single-click completion of available tasks. This also diverges from other entry points (e.g. KappaTaskRow) that still call markTaskComplete directly on available tasks, creating inconsistent behavior. If the forced Accept→Complete flow is intended, align the other completion paths; otherwise retain a direct-complete affordance for available tasks.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 resolved / 3 findings

Adds an explicit active task lifecycle state across APIs, UI, and storage to distinguish accepted tasks from unlocked ones. However, the changes cause issues with legacy accepted tasks locking downstream requirements, incorrect state handling for imported task starts, and one-click completion removal on task cards.

⚠️ Edge Case: Legacy accepted tasks lock downstream 'active'-requirement tasks

📄 app/stores/taskAvailability.ts:29-38 📄 app/stores/taskAvailability.ts:40-54 📄 app/utils/taskStatus.ts:54-57

matchesActiveStatus in taskAvailability.ts no longer falls back to isUnlockable, and isTaskActive now returns true only when completion.active===true. Legacy accepted tasks (stored incomplete with no active field, which the PR intentionally keeps as "unknown") therefore no longer satisfy a downstream task's ['active']/accept/accepted requirement. Any task B whose taskRequirement points at such a prerequisite A will flip from available to locked for existing users who accepted A before this migration, silently hiding tasks until they re-accept. Consider treating an unknown legacy prerequisite as still satisfying an active requirement (e.g. keep an unlockable/present fallback for missing-active records) or backfilling active state for incomplete legacy rows that already gate downstream tasks.

💡 Bug: Imported task-start restarts failed tasks, clobbering failed state

📄 app/composables/useEftLogsImport.ts:245-259 📄 app/composables/useEftLogsImport.ts:276-277

shouldStartImportedTask changed from !alreadyCompleted && !flags.complete && !flags.failed to !alreadyCompleted && (!flags.complete || flags.failed), so a task currently in the failed state (complete=true, failed=true) now returns true and applyStartedImports calls store.setTaskActive on it. Because applyStartedImports runs after applyCompletedImports, a task that was just marked failed (e.g. via a failed requirement) but also appears in startedTaskIds will have its failed state overwritten with active. If restarting failed tasks is intended, confirm the ordering doesn't erase failures derived within the same import; otherwise exclude failed tasks from the started set.

💡 Quality: Available tasks can no longer be completed in one click on card

📄 app/features/tasks/TaskCardActions.vue:39-53 📄 app/features/tasks/TaskCard.vue:948-959

TaskCardActions now shows an 'Accept' button (emits 'active') for state==='available' and only shows 'Complete' for state==='active'. Users must now Accept then Complete, a two-step change from the previous single-click completion of available tasks. This also diverges from other entry points (e.g. KappaTaskRow) that still call markTaskComplete directly on available tasks, creating inconsistent behavior. If the forced Accept→Complete flow is intended, align the other completion paths; otherwise retain a direct-complete affordance for available tasks.

🤖 Prompt for agents
Code Review: Adds an explicit active task lifecycle state across APIs, UI, and storage to distinguish accepted tasks from unlocked ones. However, the changes cause issues with legacy accepted tasks locking downstream requirements, incorrect state handling for imported task starts, and one-click completion removal on task cards.

1. ⚠️ Edge Case: Legacy accepted tasks lock downstream 'active'-requirement tasks
   Files: app/stores/taskAvailability.ts:29-38, app/stores/taskAvailability.ts:40-54, app/utils/taskStatus.ts:54-57

   matchesActiveStatus in taskAvailability.ts no longer falls back to isUnlockable, and isTaskActive now returns true only when completion.active===true. Legacy accepted tasks (stored incomplete with no `active` field, which the PR intentionally keeps as "unknown") therefore no longer satisfy a downstream task's `['active']`/`accept`/`accepted` requirement. Any task B whose taskRequirement points at such a prerequisite A will flip from available to locked for existing users who accepted A before this migration, silently hiding tasks until they re-accept. Consider treating an unknown legacy prerequisite as still satisfying an active requirement (e.g. keep an unlockable/present fallback for missing-active records) or backfilling active state for incomplete legacy rows that already gate downstream tasks.

2. 💡 Bug: Imported task-start restarts failed tasks, clobbering failed state
   Files: app/composables/useEftLogsImport.ts:245-259, app/composables/useEftLogsImport.ts:276-277

   shouldStartImportedTask changed from `!alreadyCompleted && !flags.complete && !flags.failed` to `!alreadyCompleted && (!flags.complete || flags.failed)`, so a task currently in the failed state (complete=true, failed=true) now returns true and applyStartedImports calls store.setTaskActive on it. Because applyStartedImports runs after applyCompletedImports, a task that was just marked failed (e.g. via a failed requirement) but also appears in startedTaskIds will have its failed state overwritten with active. If restarting failed tasks is intended, confirm the ordering doesn't erase failures derived within the same import; otherwise exclude failed tasks from the started set.

3. 💡 Quality: Available tasks can no longer be completed in one click on card
   Files: app/features/tasks/TaskCardActions.vue:39-53, app/features/tasks/TaskCard.vue:948-959

   TaskCardActions now shows an 'Accept' button (emits 'active') for state==='available' and only shows 'Complete' for state==='active'. Users must now Accept then Complete, a two-step change from the previous single-click completion of available tasks. This also diverges from other entry points (e.g. KappaTaskRow) that still call markTaskComplete directly on available tasks, creating inconsistent behavior. If the forced Accept→Complete flow is intended, align the other completion paths; otherwise retain a direct-complete affordance for available tasks.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant