Skip to content

fix(heartbeat): Copilot-review follow-ups — inactive-staleness hint, zero-day thresholds, symlink dedup - #181

Open
abhinav-phi wants to merge 3 commits into
mex-memory:mainfrom
abhinav-phi:fix/heartbeat-staleness-hint
Open

fix(heartbeat): Copilot-review follow-ups — inactive-staleness hint, zero-day thresholds, symlink dedup#181
abhinav-phi wants to merge 3 commits into
mex-memory:mainfrom
abhinav-phi:fix/heartbeat-staleness-hint

Conversation

@abhinav-phi

@abhinav-phi abhinav-phi commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Resolves #40, #41, and #42 — the three follow-ups from the Copilot review on PR #39, all touching the heartbeat/drift scan surface.

#41 — surface inactive staleness checks

checkHeartbeat returns filesWithoutLastUpdated (additive, absent once any file opts in) when every scanned scaffold file lacks a parseable last_updated. mex heartbeat prints the skip-hint with opt-in instructions; mex doctor's Heartbeat line notes the inactive checks. Exit codes and ok untouched.

#42 — accept zero for day-based thresholds

staleDays, memoryCleanupDays, dailyMemoryRetentionDays now accept 0 ("stale as soon as older than today"); negatives and garbage still fall back to the default. watch.intervalMinutes keeps positive validation. Documented on HeartbeatConfig.

#40 — deduplicate symlinked scaffold files

Both scaffoldHeartbeatFiles and findScaffoldFiles deduplicate by realpath, so one file reachable through two patterns or a symlinked directory is scanned once. Glob bounds symlink loops; the duplicate-entry risk was the real defect.

Tests

10 heartbeat tests (incl. symlink-dedup with a Windows no-privilege skip guard) + updated config parsing tests; npm run typecheck green.

Staleness is opt-in via last_updated frontmatter, so a legacy scaffold
where no file carries the field makes mex heartbeat report a healthy
sweep that in fact checked nothing. When every scanned scaffold file
lacks a parseable last_updated, the result now carries
filesWithoutLastUpdated and the CLI (and doctor's Heartbeat line) say
staleness checks are currently skipped and how to opt files in.

The hint never turns a healthy heartbeat into a failure, and the field
is absent once any file opts in, so existing v0.2 scaffolds and JSON
consumers are unchanged.

Resolves mex-memory#41
heartbeat.staleDays, memoryCleanupDays, and dailyMemoryRetentionDays
used positive-number validation, so 0 was rejected and the default
applied — config could not express 'stale as soon as older than today'.

Day-based heartbeat thresholds now accept 0 while still rejecting
negatives and other garbage (watch.intervalMinutes keeps its positive
validation: a zero-minute watch poll makes no sense). Behavior is
documented on HeartbeatConfig: staleDays 0 flags everything dated
before today; dailyMemoryRetentionDays 0 flags yesterday's daily
memory file.

Resolves mex-memory#42
@abhinav-phi

Copy link
Copy Markdown
Contributor Author

Added the #42 fix to this branch as a second commit — both issues come from the same Copilot review thread on PR #39 and touch the same heartbeat/config surface:

  • config: day-based heartbeat thresholds (staleDays, memoryCleanupDays, dailyMemoryRetentionDays) now accept 0 while still rejecting negatives — config can express "stale as soon as older than today". Behavior documented on HeartbeatConfig.
  • tests: zero-threshold cases prove a file dated yesterday is stale at staleDays: 0 and a file dated today is not; config parsing test covers 0-accepted/negative-rejected.

Resolves #42 as well.

@abhinav-phi abhinav-phi changed the title feat(heartbeat): surface inactive staleness checks on legacy scaffolds feat(heartbeat): inactive-staleness hint (#41) + zero-day thresholds (#42) Sep 8, 2026
scaffoldHeartbeatFiles and the drift scanner glob with follow: true to
support symlinked scaffold content, but a file reachable through two
patterns (or a symlinked directory) could produce two scan entries —
double heartbeat staleness lines and double drift checks.

Both discovery paths now deduplicate by realpath before returning, and
a focused test proves one file reached through two directories counts
exactly once. Glob itself bounds symlink loops, so runaway scans were
already impossible; the duplicate-entry risk was the real defect (mex-memory#40).
@abhinav-phi

Copy link
Copy Markdown
Contributor Author

Third commit adds the #40 fix (same review thread, same surface):

  • scaffoldHeartbeatFiles (heartbeat) and findScaffoldFiles (drift) both glob with follow: true but could return one file twice when it is reachable through two patterns or a symlinked directory — double staleness lines, double drift checks.
  • Both discovery paths now deduplicate by realpath before returning; glob itself bounds symlink loops, so the duplicate-entry risk was the real defect.
  • Focused test: one file reached through two directories counts exactly once.

Resolves #40 as well — this branch now closes all three Copilot-review follow-ups (#40, #41, #42).

@abhinav-phi abhinav-phi changed the title feat(heartbeat): inactive-staleness hint (#41) + zero-day thresholds (#42) fix(heartbeat): Copilot-review follow-ups — inactive-staleness hint, zero-day thresholds, symlink dedup Sep 8, 2026
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.

Handle symlinked scaffold files safely in heartbeat and drift scans

1 participant