Skip to content

feat(dependencies): RFC 9253 per-endpoint blocking + status lifecycle categories#19

Open
renatomen wants to merge 24 commits into
mainfrom
feat/rfc-status-lifecycle
Open

feat(dependencies): RFC 9253 per-endpoint blocking + status lifecycle categories#19
renatomen wants to merge 24 commits into
mainfrom
feat/rfc-status-lifecycle

Conversation

@renatomen

@renatomen renatomen commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

Implements RFC 9253 per-endpoint task dependencies on top of an always-on status
lifecycle. A dependency edge now carries a relationship type — finish-to-start,
start-to-start, finish-to-finish, start-to-finish — and blocking is evaluated
per endpoint: FS/SS gate a task's start, FF/SF gate its finish. An edge
releases the moment the predecessor reaches the point the relationship requires —
started for a start-to-* edge, completed for a finish-to-* edge — so a task
reads "Blocked · start" or "Blocked · finish" only while it is genuinely
constrained.

This is a prototype for maintainer review; do not merge.

What's included

  • Status lifecycle categories — every status has a category (Not started /
    Started / Completed), shown as a pill in the status settings, replacing the
    previous binary "Completed" toggle. Existing completed statuses stay Completed;
    the rest default to Not started. Load-time migration reconciles the
    isCompleted ⇄ category invariant both ways and is idempotent.
  • Per-endpoint blocking compute in DependencyCache, threaded onto TaskInfo
    as startBlocked/finishBlocked (+ reverse) through both assembly sites
    (native and Bases-rendered).
  • Honest card surfaces — the secondary badge, metadata pill, property pill,
    and CodeMirror editor widget all show the computed state (red "Blocked · start",
    amber "Blocked · finish", muted "Dependencies" when released). The count reflects
    only currently-constraining predecessors, so a released edge is no longer counted
    as blocking.
  • Both-sided authoring (opt-in) — set a relationship type and an optional lag
    per dependency from both the "Blocked by" and "Blocking" lists, with a
    plain-language summary naming both tasks so direction is unambiguous. Editing a
    Blocking edge writes the change to the blocked task's own frontmatter (the
    canonical location). Row layout stacks on narrow/mobile widths.
  • Features opt-in — an "Advanced dependency types" toggle (off by default)
    gates the authoring controls.
  • Bases — TaskNotes-rendered Bases views reflect the computed state; native
    .base blocked-by filters remain existence-only (documented — a per-note filter
    formula cannot read a predecessor's status).

Verification

  • Full unit suite green except 7 pre-existing timezone-sensitive suites that fail
    identically on main (dateUtils, ics-export, ICSNoteService, issues
    384/385/810/1108/2006).
  • Every surface was verified live in a dogfood vault (badge/pill states, the
    blocked→released transition, both-sided authoring persistence, the mobile modal
    layout, and the Features toggle).
  • An independent multi-model review pass (correctness / adversarial / API-contract
    / maintainability) found no P0/P1 issues; the P2/P3 findings are addressed in the
    final fix(review) commit (gap edge cases, an isStarted robustness fallback, a
    shared deriveBlockingState to remove duplication, and pessimistic handling of
    broken-link blockers).

Notes for the maintainer (product decisions)

  1. Status categories are always-on, not gated behind the advanced-dependencies
    opt-in. This is deliberate — the lifecycle also underpins future group-by-status
    — but it means a custom "in progress" status must be categorized Started for
    start-based dependencies to release when it begins (flagged in the changelog).
  2. The "lag" value is stored but not yet evaluated. The RFC-9253 gap field is
    authored and persisted, but release is currently purely status-based — a 2-day
    lag does not hold a successor for 2 days after the predecessor finishes.
    Open question: keep it for RFC completeness/interop, hide it until
    lag-aware scheduling exists, or implement date-based lag.

Add an optional StatusCategory (planned | in-progress | completed) to
StatusConfig, kept in sync with the load-bearing isCompleted flag rather
than replacing it. A load-time normalization backfills category=completed
for already-completed statuses and leaves everything else uncategorized
(no guess). StatusManager gains isStarted/isFinished/getCategory so the
dependency cache can evaluate start-anchored constraints.
Replace the binary completed toggle in the status config card with a
category dropdown (Uncategorized / Planned / In progress / Completed) and
show the chosen category on the card pill, mirroring how the completed
badge rendered before. Selecting Completed keeps isCompleted in sync;
migrated statuses render a neutral Uncategorized nudge. Adds category pill
styles and the i18n strings.
- Reconcile the isCompleted <-> category invariant in both directions in
  normalizeStatusCategories, so a completed status can never read as
  finished-but-not-started once the lifecycle predicates gain consumers.
- Fold the duplicated categoryOptions strings into the shared badges keys
  the pill already uses; drop the orphaned completed toggle strings.
- Cover the migration through the real settings load path, and add the
  completed-with-stale-category reconciliation case.
- Record the status settings change in the unreleased changelog.
@renatomen renatomen closed this Jul 23, 2026
@renatomen renatomen reopened this Jul 23, 2026
… Completed)

Drop the fourth 'Uncategorized' state per the product decision. Every status
now carries one of three lifecycle categories, guaranteed at runtime: the
load-time normalizer defaults any non-completed status to Not started, and the
two settings write sites (add-status button, createDefaultStatus) seed a
category so nothing uncategorized is reachable. Relabel the enum's user-facing
strings to Not started / Started / Completed; isCompleted stays the derived
Completed mirror.
@renatomen renatomen closed this Jul 23, 2026
@renatomen renatomen reopened this Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0717329e2e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +234 to +236
customStatuses: normalizeStatusCategories(
loadedData?.customStatuses || DEFAULT_SETTINGS.customStatuses
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Persist normalized lifecycle categories during migration

For existing settings, this produces the new categories only in memory, but does not mark the load as migrated in shouldPersistMigratedSettings. Consequently, a vault whose only outdated setting is a legacy customStatuses array keeps writing no category values to data.json unless the user later changes and saves a setting; the category migration is repeated on every load and the newly introduced lifecycle data is never actually persisted. Include a category-migration condition in the persistence flag.

Useful? React with 👍 / 👎.

…cyCache

Replace the reltype-blind completion-only gating with RFC 9253 per-endpoint
constraints: FS/SS gate a task's start, FF/SF gate its finish, and each edge
releases when its predecessor reaches its own endpoint (completed for FINISH*,
started for START*). The cache stores each edge's reltype and both the finished
and started lifecycle per path, computing startBlocked/finishBlocked on demand;
the relationship fingerprint keys on reltype so a reltype-only edit re-indexes,
and the file signature keys on the per-endpoint blocked membership so a
predecessor's lifecycle move re-fires the change event. Exposes
isTaskStartBlocked/isTaskFinishBlocked plus reverse per-endpoint accessors.
Add startBlocked/finishBlocked and the reverse isBlockingStart/isBlockingFinish
to TaskInfo, sourced from the cache's per-endpoint accessors at both assembly
sites (TaskManager, bases/helpers); isBlocked stays as their OR for existing
consumers. The cache-absent fallback approximates start/finish-blocked from the
task's own edge reltypes (existence-based). This is the contract U5 (surfaces)
and U8 (Bases) consume.
@renatomen renatomen closed this Jul 23, 2026
@renatomen renatomen reopened this Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bada0226e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +350 to +353
edges = new Map();
this.edgeReltypes.set(dependentPath, edges);
}
edges.set(blockingPath, reltype);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve all relation types for a predecessor

When a task has multiple dependency entries targeting the same predecessor with distinct relationship types, this map retains only the last one. Such entries are supported by the context-menu deduplication key, which includes reltype; for example, an unfinished predecessor with both FINISHTOSTART and FINISHTOFINISH edges should block both endpoints, but whichever entry appears last causes the other endpoint to be reported as unblocked. Store a collection of relation types per predecessor (or otherwise evaluate every normalized entry) instead of overwriting it.

Useful? React with 👍 / 👎.

…accessors

The calendar-filter test mocks dependencyCache with only the legacy accessors;
TaskManager's assembly now also calls isTaskStartBlocked/isTaskFinishBlocked and
the reverse dependent-path accessors (U4), so the partial mock threw and the
task dropped out of the filtered set. Add the missing methods to the mock.
@renatomen renatomen closed this Jul 23, 2026
@renatomen renatomen reopened this Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad0cfb0b79

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +284 to +287
categorySelect.addEventListener("change", () => {
const value = categorySelect.value as StatusCategory;
status.category = value;
status.isCompleted = value === "completed";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh dependency state after category changes

When a user changes a non-completed status from Not started to Started in the settings UI, this only updates StatusManager; the dependency cache is not rebuilt because its settings snapshot does not include customStatuses. As a result, the cached startedStatusByPath remains false for tasks using that status, so existing STARTTOSTART and STARTTOFINISH dependencies remain blocked until a task frontmatter change or plugin reload rebuilds the cache.

Useful? React with 👍 / 👎.

Comment on lines +358 to +361
private isEdgeReleased(predecessorPath: string, reltype: TaskDependencyRelType): boolean {
return reltypeReleasedByPredecessorFinish(reltype)
? (this.completedStatusByPath.get(predecessorPath) ?? false)
: (this.startedStatusByPath.get(predecessorPath) ?? false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve lifecycle state for non-task predecessors

A dependency may resolve to any markdown file, but index construction only populates lifecycle maps for files identified as tasks. Therefore, on an initial cache build, a task blocked by a non-task note whose frontmatter has a completed/started status reaches this fallback and is treated as unreleased; FINISHTOSTART/FINISHTOFINISH edges remain blocked even though the predecessor status satisfies them. Read and cache the predecessor lifecycle on demand (as the previous completion path did), or index resolved non-task predecessors as well.

Useful? React with 👍 / 👎.

Wire the task-card secondary badge, the metadata pill, and the editor
widget to the computed start/finish-blocked state instead of raw
blocked-by existence. A task reads "Blocked · start" when it cannot
begin and "Blocked · finish" when it can begin but cannot complete;
start wins over finish so the "can I act now?" signal stays unambiguous.

The badge and pill count only currently-constraining predecessors, so a
released edge (started SS, completed FS) drops the blocked label and
shows as a listed-but-satisfied dependency rather than "Blocked (1)".

DependencyCache gains getStart/FinishBlockingPredecessorPaths (the
forward constraining view); taskCardRelationships.resolveBlockedConstraint
is the shared honest-signal resolver every surface reads.
…modal

Add per-row relationship controls to the task edit modal's Blocked-by
and Blocking lists (gated by a new enableAdvancedDependencyTypes setting,
off by default): a reltype dropdown, a structured lag (number + unit
composed to an ISO-8601 duration), and a plain-language summary that
names both concrete tasks so the direction can't be read backwards.

Blocking rows source their real reltype/gap from the blocked task's own
blockedBy (the canonical location) instead of assuming the default, and
editing a Blocking edge writes the change back there — TaskService gains
a "modify" action alongside add/remove, and the edit-change diff now
reports modified edges. An exotic stored lag stays read-only so an edit
never silently rewrites it. The row layout stacks on narrow and mobile
widths.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c7a3b27af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/settings/defaults.ts
// Relationships widget defaults (unified subtasks, projects, and dependencies)
showRelationships: true,
relationshipsPosition: "bottom",
enableAdvancedDependencyTypes: false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Expose the advanced relationship toggle

enableAdvancedDependencyTypes defaults to false, and at this revision its only other source references are its type declaration and the read in TaskModal; no settings control or documented configuration path can set it to true. Consequently, normal users can never reveal the newly announced reltype/lag controls, so every dependency remains locked to the default UI unless they manually edit plugin data. Add a user-accessible setting (or enable the controls by default) before advertising this capability.

Useful? React with 👍 / 👎.

Comment on lines +359 to +361
return reltypeReleasedByPredecessorFinish(reltype)
? (this.completedStatusByPath.get(predecessorPath) ?? false)
: (this.startedStatusByPath.get(predecessorPath) ?? false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Emit dependent updates when a predecessor starts

For STARTTOSTART and STARTTOFINISH edges, this new branch releases dependents when the predecessor changes from Planned to In progress. However, emitTaskUpdateEvents in src/services/task-service/taskPropertyChangeSideEffects.ts still refreshes dependent task cards only when isCompleted changes, so this transition emits no update for the affected successors. Open task cards/Bases can therefore continue showing “Blocked · start/finish” until another refresh occurs; treat a started-state transition like the existing completion transition when notifying dependent tasks.

Useful? React with 👍 / 👎.

Surface a toggle for enableAdvancedDependencyTypes under a new
Dependencies group in the Features tab, mirroring the existing
feature-toggle pattern. Off by default; turning it on reveals the
reltype/lag authoring controls in the task edit modal.
…t filter limit

TaskNotes-rendered Bases views (Task List, Kanban, cards) already carry
the computed start/finish-blocked state from the cache. Add coverage for
those fields, and document why a native .base filter on blocked-by stays
existence-only: a filter formula evaluates one note's frontmatter and
cannot read a predecessor's status, so it cannot tell a released edge
from a live one. Update the dependencies docs to describe per-endpoint
blocking and point users to a TaskNotes-rendered view for constraint-
aware display.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c112e849f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +646 to +647
const { start, finish } = this.computeBlockedDependents(taskPath);
return Array.from(new Set([...start, ...finish]));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retain released START dependents in the blocking editor*

For a STARTTOSTART or STARTTOFINISH edge, once the predecessor enters an in-progress status, this returns no dependent paths because the edge is released. TaskManager assigns this result to task.blocking, and TaskEditModal builds its “Blocking” list from that field, so opening the predecessor no longer shows the still-persisted relationship and gives users no way to edit or remove it. Keep an all-dependents accessor for relationship management, while using the release-aware accessors only for the blocking signal.

Useful? React with 👍 / 👎.

- composeDependencyGap: reject a sub-1 value (was composing a "P0D" no-op)
  and an absurd value that would serialize in scientific notation; the lag
  input gains step/max so fractional entry is prevented at the source.
- StatusManager.isStarted: treat a completed status as started via
  isCompletedStatus, robust to a status config that was not normalized.
- Extract deriveBlockingState so the TaskManager and Bases assembly sites
  share one per-endpoint derivation instead of duplicating it verbatim.
- A broken-link (unresolved) blocker now holds the card pessimistically
  blocked rather than reading as a satisfied "Dependencies" relationship.
- Note the last-wins reltype for a hand-authored duplicate edge.

Tests: blocking-edge modify detection in the edit-change diff, reverse
accessors dropping a released successor, gap edge cases, and the
unresolved-blocker state. Changelog: flag that a custom in-progress
status must be categorized Started to release start-based dependencies.
@renatomen renatomen changed the title feat(status): status lifecycle categories (Planned / In progress / Completed) feat(dependencies): RFC 9253 per-endpoint blocking + status lifecycle categories Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5caf3d2bec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +648 to +649
const { start, finish } = this.computeBlockedDependents(taskPath);
return Array.from(new Set([...start, ...finish]));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve released dependents for completion notifications

When a predecessor is marked completed through the property-change path, emitTaskUpdateEvents calls cacheManager.getBlockedTaskPaths after the cache has incorporated the new lifecycle state. This accessor now excludes every edge released by that completion, so finish-to-start and finish-to-finish successors are absent and receive no EVENT_TASK_UPDATED; their open cards/Bases can retain the old blocked state until another refresh. Keep a non-release-filtered dependent accessor for notifications, while retaining this release-aware result for the blocking UI.

Useful? React with 👍 / 👎.

…ders

The blocked-by and blocking relationship lists tracked their expanded
state only in the DOM, so any card re-render (a related task update, a
view refresh) collapsed them with nothing to restore. Move that state
into ExpandedProjectsService alongside the subtasks expansion and
re-open the list on render when the service says it was open.
… types

Non-gating guidance so users configure statuses for the feature to work:
- A status-readiness summary under Advanced Dependencies in Features shows
  the per-category counts and whether start-based (start-to-start /
  start-to-finish) dependencies can release, since those need a Started
  status to release before completion.
- An inline advisory in the status settings flags a missing category while
  advanced types are on, at the point statuses are edited.

Enablement is never blocked: a degraded state is safe (start-based edges
just fall back to releasing on completion), so this guides rather than gates.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8eb7de5d95

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 298 to 301
const resolved = resolveDependencyEntry(this.app, path, dep);
if (resolved?.path && this.isValidFile(resolved.path)) {
this.addDependencyLink(path, resolved.path, blockingTasks);
this.addDependencyLink(path, resolved.path, dep.reltype, blockingTasks);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply dependency lag when computing blocking state

For every nonzero lag entered in the new controls (for example P2D), the cache stores only dep.reltype here and isEdgeReleased subsequently checks only the predecessor's started/completed state. Thus a finish-to-start edge with a two-day lag is released immediately when its predecessor completes, exactly like an edge with no lag; the newly exposed lag is persisted but has no effect on the advertised dependency constraint.

Useful? React with 👍 / 👎.

When a task gates a successor's finish, the blocking toggle tooltip now
reads "Blocking N tasks: X to start, Y to finish" instead of a flat
count, mirroring the per-endpoint blocked-by badge. A finish-to-start-only
case (no finish-gated successors) keeps the plain summary.
Obsidian tooltips render with white-space: pre-wrap, so put the start and
finish totals each on their own line for readability.
…ncy quick path

Eight plain-language menu titles (four types x blocking/blocked-by) for the context-menu type-before-task step: readable arrow names plus a direction-specific clause, no raw FS/SS/FF/SF codes.
…u quick path

When advanced dependency types are enabled, Dependencies -> Blocking / Blocked by lists the four relationship types before the task picker, and the edge is created with the chosen type. The shared task picker is reused unchanged and toggle-off behavior is identical to before. The reltype-aware add is extracted into taskContextMenuDependencies.ts so the threading is unit-tested.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b9ad5ee40

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +313 to 322
categorySelect.addEventListener("change", () => {
const value = categorySelect.value as StatusCategory;
status.category = value;
status.isCompleted = value === "completed";
const metaContainer = statusCard?.querySelector(".tasknotes-settings__card-meta");
if (metaContainer) {
metaContainer.empty();
if (status.isCompleted) {
metaContainer.appendChild(
createStatusBadge(
translate("settings.taskProperties.taskStatuses.badges.completed"),
"completed"
)
);
}
metaContainer.appendChild(createCategoryBadge(status.category, translate));
}
save();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Rebuild task indexes when lifecycle categories change

When a user changes a status category to or from completed, this handler saves the setting but the settings lifecycle does not treat customStatuses as cache-affecting, so TaskManager.updateConfig() is not called. Any already-built filter indexes retain their old isCompleted values (used by getOverdueTasks and completed/active task queries), causing tasks with that status to remain incorrectly included or excluded until a frontmatter change or reload rebuilds the indexes.

Useful? React with 👍 / 👎.

…methods

The card now calls isBlockingExpanded / isBlockedByExpanded / setBlockingExpanded / setBlockedByExpanded (added with expansion persistence in ee33f38), but these two suites still mocked only isExpanded/toggle, so they threw on CI and failed the coverage gate. Complete the mocks to match the service.
… module

addReltypeMenuItems lived on TaskContextMenu where the Obsidian Menu made it awkward to unit-test, leaving the new menu-wiring below the new-code coverage bar. Move it into taskContextMenuDependencies.ts behind a minimal Menu seam and cover the type ordering, side-specific titles, and reltype threading.
…lazy build

getStartBlockingPredecessorPaths / getFinishBlockingPredecessorPaths and the build-indexes-on-first-query guard were unexercised, holding the branch's new-code coverage under the 80% gate. Add two DependencyCache cases so they run.
The blocked-by / blocking sub-list was rebuilt (clear + refetch + re-render)
on every card render via restoreRelationshipExpansion, so a Bases TaskList /
Kanban card recreated on EVERY_TASK_UPDATED re-fired the async expansion and
oscillated open/closed on frontmatter edits. Expansion is now DOM-only: built
when the user expands, not restored on later renders (matching main's behavior).
…stence

With the render-time restore gone, ExpandedProjectsService's blocked/blocking
expansion tracking had only dead reads and write-only setters. Remove the two
sets, their four accessors, and the rename/clear plumbing, plus the setter call
sites in the toggle handlers. Blocked/blocking expansion is DOM-only now, so the
handlers no longer need a plugin reference. Deletes the obsolete service test.
Activating a Bases leaf re-fires the onDataUpdated lifecycle with unchanged
data, and the base scheduled a full re-render (empty the items container and
recreate every card) unconditionally. That tore down a card's expanded
blocked-by / blocking sub-list on a mere click or focus change, since the
expansion is DOM-only and not restored on re-render.

Gate the re-render on a data signature the same way CalendarView already does:
when the signature is unchanged since the last render, skip. Implemented on the
base so both TaskListView and KanbanView are covered.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d8be168f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +71 to +73
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add the selected dependency after deduplicating legacy entries

When the existing blockedBy array already contains duplicate or invalid entries, adding a distinct new task can make combined.length equal existing.length because normalization removes an old entry while adding the new one. This early return then drops the user's requested dependency entirely (for example, two identical legacy blockers plus one newly selected blocker), rather than persisting the normalized list. Compare the actual normalized entries or the selected dependency's presence instead of only comparing lengths.

Useful? React with 👍 / 👎.

@sonarqubecloud

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant