Skip to content

A pass must be able to label its own source #94

Closed

Description

@kridaydave

Question

runOrganizePass() hardcodes source: "manual" in its history write, so a pass launched by cron reports itself as manual. Nothing in src/ can produce the string scheduled, even though the type, both history query filters, and the view_history markdown output all already accept and display it.

The defect, exactly:

  • src/extensions/scheduler/organize-pass.ts:113 writes source: "manual" as a literal.
  • src/server.ts:167 writes source: "manual" for every MCP tool call. Correct today, since a tool call is manual by definition.
  • HistoryEntry.source is "manual" | "scheduled" (src/services/history-logger.service.ts:23).
  • SearchHistoryInputSchema.source accepts both (src/schemas/system.ts:115).
  • ViewHistoryInputSchema.source accepts both (src/schemas/system.ts:58).

So the value is declared, filterable, and displayable, and unreachable.

Consequence. Every consumer that wants "what did my schedule do" gets nothing.

  • #49 organizer_digest filtering on source: "scheduled" matches nothing.
  • #31 watch_status cannot tell a scheduled pass from an agent's organize_files.
  • view_history with source: "scheduled" returns an empty list on a system used purely on a timer, which is a silent wrong answer rather than an error.

Decision already taken during charting, this ticket records the implementation. A process cannot detect that cron started it. The caller knows, so the caller says. Add a --source flag to once, defaulting to manual, and thread it into runOrganizePass.

Implementation detail.

  1. src/extensions/scheduler/watch.schemas.ts holds the existing flag schemas for the watch CLI. Add the source flag there rather than parsing by hand in once-cli.ts.
  2. parseOnceFlags (src/extensions/scheduler/once-cli.ts:57) rejects unknown - prefixed flags. Add --source manual|scheduled to the known set there.
  3. Add source?: "manual" | "scheduled" to OrganizePassOptions (organize-pass.ts:21), defaulting to "manual" so every existing caller is unchanged.
  4. Replace the literal at organize-pass.ts:113 with the resolved value.
  5. Document the crontab line in whatever doc tells users how to schedule a pass. The crontab is the thing that knows, so the flag belongs there. Example shape: */15 * * * * file-organizer-watch once --apply --source scheduled ~/Downloads.

Do not add a third enum value. That is ruled out, see the map's Out of scope.

Tests that must fail without this change.

  • organize-pass.ts with source: "scheduled" produces a history row whose source is scheduled. Assert on the row, not on a spy.
  • once-cli.ts rejects --source bogus as a usage error, matching how it treats other unknown flags.
  • once --source scheduled --dry-run writes no row at all, since the dry-run early return at organize-pass.ts:106 must stay before the history write.

Proof. node .opencode/skills/verify-file-organizer/scripts/control-file-organizer.mjs in a sandbox, reading back operations.jsonl after driving once --apply --source scheduled.

Blocks. organizer_digest and file_organizer_watch_status both need a truthful source to filter on.

Activity

  1. self-assigned this
    on Oct 7, 2026
  2. kridaydave commented on Oct 9, 2026

    @kridaydave
    OwnerAuthor

    Resolution

    Landed in #88, commit a1178c8 ("add --source flag to the once command so cron passes can label their history rows"). The branch also carried the EXIF calendar fix; kriday chose to merge the two together, and the PR body documents both.

    The decision. A process cannot detect that cron started it, so the caller says. --source on the once command is how a crontab line asserts it. No third enum value: the flag takes the same two values HistoryEntry.source already had.

    Implementation.

    • OnceSourceSchema in src/extensions/scheduler/watch.schemas.ts, a z.enum(["manual", "scheduled"]). This is the one place the assertion is checked, so the CLI and OrganizePassOptions read the same type.
    • parseOnceFlags in once-cli.ts iterates by index now, so --source can consume the argument after it. A for...of would have read that value as a second directory and rejected a good command line.
    • OrganizePassOptions.source, optional, defaults to "manual", so every existing caller is unchanged.
    • runOrganizePass writes the resolved value where it used to write the literal.

    Tests, in tests/integration/once-single-pass.test.ts: the scheduled row, the manual default, no row at all for a scheduled dry run, and --source bogus plus --source with no value as usage errors.

    Proof on the real server, sandbox config under XDG_CONFIG_HOME, data under tests/sandbox/ (gitignored, removed after):

    • dry run with --source scheduled: exit 0, historyLogged: false, no row in operations.jsonl
    • --apply --source scheduled: exit 2, row {"source":"scheduled","status":"success","filesProcessed":2}
    • a later pass with no flag: row {"source":"manual","filesProcessed":0}
    • --apply --source bogus: usage error, exit 1, no pass run
    • file_organizer_search_history with source: "scheduled" driven over stdio on the real server: 1 of 1 entries returned, the scheduled one. That filter is what organizer_digest #49 and file_organizer_watch_status #31 needed, and it now matches something.

    The gap this ticket describes as unreachable is closed: scheduled is producible, filterable, and displayed.

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

Metadata

Metadata

Assignees

Labels

wayfinder:taskWayfinder map / decision ticket

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions