Skip to content

Merge dev → main (automated) - #2011

Merged
SorraTheOrc merged 62 commits into
mainfrom
release/dev-to-main-20260726135209
Jul 26, 2026
Merged

Merge dev → main (automated)#2011
SorraTheOrc merged 62 commits into
mainfrom
release/dev-to-main-20260726135209

Conversation

@SorraTheOrc

Copy link
Copy Markdown
Member

Automated release created by ship skill.\n\nIncludes CHANGELOG.md with work-item summaries from this release.

Sorra the Orc added 30 commits July 12, 2026 01:52
…local update is newer

In mergeDifferentTimestampItems, gate the remote close-preservation
behind isRemoteNewer. Previously, if the remote had completed/done
status/stage and the local had any other value, the remote close was
always preserved regardless of which side was newer — making it
impossible to reopen a closed work item through the normal sync
workflow.

Now: when local has a newer updatedAt and intentionally reopens
(e.g., open/in_progress), the remote close-preservation is skipped
and normal timestamp-based resolution takes over, allowing the
reopen to be respected.

mergeSameTimestampItems is unchanged — close-preservation there
still serves as a tiebreaker when timestamps are identical.

Tests added for both scenarios:
- Reopen respected (local newer)
- Close preserved (remote newer)
…ommitted hooks when core.hooksPath is set

Adds core.hooksPath detection and canonical template comparison to the
hook-upgrade module. When 'git config core.hooksPath = .githooks',
git ignores .git/hooks/ and runs hooks directly from .githooks/. The
upgrade now:

1. Detects core.hooksPath via detectHooksTargetDir()
2. When hooks target is .githooks/, compares committed hooks against
   canonical templates (generated from init.ts template strings)
3. Upgrades outdated .githooks/ hooks in-place with the correct content
4. Falls back to existing .githooks/ vs .git/hooks/ comparison when
   core.hooksPath is not set

Also adds generateCanonicalHookContent() which produces the expected
hook content matching what installCommittedHooks() in init.ts generates.

Fixes: AC1-AC8
…ugin

- Add  with:
  - Minimal cron expression parser (5-field standard cron)
  - Scheduler class with background interval, idle-state tracking via pi events, and duplicate-fire prevention
  - CLI command handler (/wl schedule with list, add, remove, toggle, help)
  - Configurable tick interval (default 30s)
- Add  with 48 tests covering:
  - Cron matching (wildcards, exact, lists, ranges, steps, day-of-week 0/7)
  - Schedule entry parsing and validation
  - Scheduler lifecycle (start, stop, tick, duplicate prevention)
  - CLI command parsing (add, remove, toggle, list, error handling)
  - Cron expression validation
- Update  to include schedules in Settings interface,
  loading, persistence, and defaults
- Update  to pass through schedules in update()
- Update  to integrate scheduler: persister wiring,
  lifecycle hooks, /wl schedule subcommand handler and completions

Closes WL-0MRHYQU1S009DJ9B
The gitPushDataFileToBranch function creates a temp worktree, commits the
JSONL data file, and pushes to refs/worklog/data. The git push triggered
the repo's pre-push hook, which runs 'wl sync --git-branch refs/worklog/data',
creating a recursive loop.

Fix: add --no-verify to the git push command inside withTempWorktree so
the pre-push hook is bypassed for the internal sync push.

- src/sync.ts: Added --no-verify flag to push command in withTempWorktree
- tests/sync-worktree.test.ts: Added test verifying --no-verify is present

Closes WL-0MRIFV2OW002DDAN
Add defense-in-depth safety checks to the pre-push hook to prevent
recursive 'wl sync' invocation and graceful handling when running
inside git worktrees or temp worktrees created by withTempWorktree.

Changes:
- .githooks/pre-push: Add worktree detection (git-dir vs git-common-dir)
  and temp worktree detection (tmp-worktree- in PWD), wrap wl sync
  in error handler so failures don't abort the push
- src/commands/init.ts (installPrePushHook): Update generated hook
  script with the same safety checks
- src/doctor/hook-upgrade.ts (generateCanonicalHookContent): Update
  canonical pre-push hook template with safety checks
- tests/sync-worktree.test.ts: Add verification tests for all three
  source file changes

Closes WL-0MRIFWG8O005CLI0
…r needsProducerReview flag

- Add needsProducerReviewIcon/Label/Fallback functions to src/icons.ts
- Update WorklogBrowseItem interface with auditedAt, needsProducerReview, updatedAt
- Update normalizeListPayload and DB-backed list functions to include new fields
- Update getIconPrefix for audit-aware in_review icon and producer review column
- Update enrichWorkItem in next.ts to pass through auditedAt
- Update in-progress.ts and recent.ts for auditedAt enrichment
- Update docs/icons-design.md with new layout and audit staleness info
- Add comprehensive tests for new icon logic including staleness checks
…onitoring

Creates a new Pi agent skill that inspects the completed/in_review
queue and takes action based on queue state:

- Sparse queue (<10 items): Flags the next wl next item for producer
  review via --needs-producer-review true.
- Full queue (>=10 items): Finds the first item by sortIndex without
  a valid audit result ('Ready to close: Yes') and runs the audit
  runner on it.
- All items ready: Reports 'Project is ready for producer review
  prior to a new release'.

Files:
- skill/heartbeat/SKILL.md — Skill documentation
- skill/heartbeat/scripts/heartbeat.py — Core implementation
- tests/skill/heartbeat/test_heartbeat.py — 16 unit tests
- skill/heartbeat/__init__.py — Package marker
Add new test file tests/cli/valid-json-output.test.ts that validates every
wl built-in command with --json produces pure, parseable JSON with no
preamble or trailing non-JSON content.

Coverage:
- 29 tests passing across all command families (CRUD, subcommands,
  management, content/display, error paths, doctor)
- 7 tests skipped with documented reasons (audit, reviewed require
  harness additions; init, sync, github, migrate need special setup)

Key features:
- expectValidJson() helper ensures stdout starts with {/[ and ends with }/]
- Error-path tests for NONEXISTENT items verify success:false JSON
- Tests run in isolated temp directories via existing cli-helpers
- Documentation updated in tests/README.md to list new test file
The heartbeat skill now includes a Pi-agent-level gate that checks the
last assistant message before proceeding. If the last message clearly
indicates a process completed, the heartbeat proceeds; otherwise it
reports no action and exits.

Changes:
- skill/heartbeat/SKILL.md: Added Completion-Detection Gate section
  with step-by-step instructions for the agent to check conversation
  history before running the heartbeat logic.
- skill/heartbeat/scripts/heartbeat.py: Added --force flag for
  standalone/automated use, and parse_args() function.
- tests/skill/heartbeat/test_heartbeat.py: Added 3 tests for --force
  flag (default false, with --force true, main() integration).
…ersion() in status test

WL-0MRJB6EVH005JJ3T

- Changed hardcoded 'Version: 1.0.2' to template literal using getPackageVersion()
- Also fixed pre-existing test gap in worklog-browse-extension.test.ts by adding
  the 'schedule' stage completion that was added to the TUI extension but
  not reflected in test expectations
- Add reviewedCommand import to cli-inproc.ts (was missing, causing skip)
- Replace skipped reviewed tests with real tests (2 tests)
- Add audit-set --json test (covers audit-result module set subcommand)
- Add doctor migrate --json and doctor stage-sync --json tests
- Add migrate sort-index --dry-run --json test
- Strengthen error path tests: strict first/last-char preamble detection,
  explicit success:false assertion for all 4 error paths
- Remove lenient regex matching from error paths; use pure JSON validation
- Error detection handles both output.error() and output.json() shapes
- Remove now-redundant reviewed skip from hard-to-test group (4 remain)

Test results: 34 passed / 4 skipped (0 failed). Full suite: 3178 passed.
All pre-existing failures (WL-0MRJB6EVH005JJ3T, WL-0MRJB6OKZ004H056)
are already resolved in previous commits.
…st scheduler

Updates the Worklog extension README with a comprehensive Periodic
Request Scheduler section covering:
- How the scheduler works (background ticker, idle check, cron matching)
- The /wl schedule command with subcommands (list/add/remove/toggle/help)
- Cron expression format with syntax reference table
- Common cron examples
- Configuration persistence format in .pi/settings.json
- Technical notes (file location, lifecycle, test coverage)

Also adds a CHANGELOG entry for the scheduler feature and updates
the /wl slash command documentation to mention the schedule subcommand.
…d, not per-tool

- Remove tool_execution_end handler that prematurely reset _agentBusy
- Add explanatory comment: a turn may use multiple tools, so busy
  flag must stay true until message_end
- Add schedules config to .pi/settings.json
…on list

Implements multi-key chord sequences of arbitrary depth (>2) in the
Worklog browse extension's shortcut system.

Changes:
- Add getChordByPrefix(prefix, view?, stage?) method to ShortcutRegistry
  in shortcut-config.ts for finding all chord entries matching a prefix
- Refactor browse.ts list view: replace pendingChordLeader: string | null
  with pendingChord: string[] for multi-step chord accumulation
- Refactor browse.ts detail view: replace detailPendingChordLeader with
  detailPendingChord: string[] parallel to list view
- Update chord dispatch logic in both views to use arbitrary-depth array
  accumulation with getChordByPrefix fallback for continuation
- Update help-text renderer in both views to show next-available keys
  at any chord depth using getChordByPrefix instead of getChordByLeader
- Escape at any point in a chord sequence cancels the pending chord
- Update shortcuts.json with depth-3 chords (u+p+c for critical, u+p+h for high)
- Add comprehensive tests for getChordByPrefix (prefix matching, view
  filtering, stage filtering, empty/edge cases)
- Update existing test expectations for updated shortcuts.json
…50042QC9)

- Added  check in  to throw on write failure
- Added aggregate failure tracking in
-  catches persistence errors, outputs  with
  error message and exits with non-zero code
-  catches persistence errors, surfaces
  them in results
- Added  to TUI cache-invalidation list for consistency
- Added tests for read-only database failure scenarios
- Updated docs/AUDIT_STATUS.md with error behavior documentation
…tripping (WL-0MRMG8P2X004V6YC)

Extract shared formatChordHints() helper to deduplicate pending chord
hint rendering between browse list and detail view.

Changes:
- Add formatChordHints() exported function that groups chords by nextKey
  and collapses multi-entry groups to <nextKey>:<firstWord>... pattern
- Strip consumed words based on actual pendingChord.length instead of
  always stripping exactly 1 word
- Add safety guard for labels with fewer words than chord depth
- Both browse list and detail view now use formatChordHints()
- 12 new tests covering all formatting scenarios

Fixes: help line at 'u' layer now shows 'p:priority...' instead of
'p:priority low p:priority medium p:priority high p:priority critical'.
At 'u-p' layer shows 'l:low m:medium h:high c:critical'.
This commit fixes a regression where blocked work items were not
automatically unblocked when all blockers were completed.

Root cause: The update() method only reconciled the items dependents
(via reconcileDependentsForTarget) when status/stage changed, but did
NOT reconcile the item itself (via reconcileDependentStatus). This
meant that when an item was completed, its dependents were checked, but
the items own status based on its blockers was not re-evaluated. The
CLI handlers compensated with an extra call, but direct DB API usage
missed this.

Fix: Added reconcileDependentStatus(id) inside update() so
both the item and its dependents are reconciled when status/stage
changes. This ensures self-reconciliation (e.g., re-blocking a reopened
item that still has active blockers) happens automatically regardless
of the caller.

Also added:
- 33 regression tests covering AC1-AC5, cache interaction, CLI flow
  simulation, and complex multi-dependent scenarios
- Updated dependency-reconciliation.md with correct line numbers
  and documentation of the new self-reconciliation step

Relates-to: WL-0MRNM8EBD000C2P1
Changes 4 .replace('<id>', ...) call sites in browse.ts to use global
regex .replace(/<id>/g, ...) so that all occurrences of the <id> token
are replaced when dispatching shortcut commands. Previously only the
first occurrence was replaced, breaking commands with multiple <id>
tokens like '!!wl reviewed <id> false && wl audit-set <id> ...'.

Also adds tests covering:
- Multi-<id> replacement in single-key dispatch
- Multi-<id> replacement in chord dispatch
- Backward compatibility with single <id> token
- Three <id> tokens in one command

Affected files:
- packages/tui/extensions/Worklog/lib/browse.ts (4 replace calls)
- packages/tui/extensions/Worklog/lib/browse.test.ts (new tests)
…A8V)

The initializeRuntime() function in src/lib/runtime.ts installs a beforeExit
handler that logs debug-level messages to stderr via console.error:

  [runtime] Received beforeExit; awaiting 0 pending task(s)...
  [runtime] All tasks complete.

These messages pollute stderr output after --json commands, breaking
interoperability for scripts consuming tool output.  Fix by passing
{ silent: true } to initializeRuntime() in src/cli.ts, which suppresses
the lifecycle log messages while preserving the awaitAll() behaviour.

Also update the valid-json-output error-path tests to use real subprocesses
(via child_process.exec) instead of the in-process harness, because the
in-process harness restores console.error before the beforeExit handler
fires, masking these messages.  The subprocess tests additionally assert
that stderr contains no [runtime] lines.

Fixes WL-0MRJ2R8LJ003LA8V (review failure: stderr noise after --json output)
…RNW3CZY006K0LD)

Update expected counts and label/description assertions after commit c1bf5a1
split the u-p chord into four distinct priority chords (u-p-l, u-p-m, u-p-h,
u-p-c), adding 2 new entries to shortcuts.json.

Changes:
- expect(entries).toHaveLength(18) -> toHaveLength(20)
- expect(entries.filter(e => e.key === '').length).toBe(13) -> toBe(15)
- expect(upChords).toHaveLength(13) -> toHaveLength(15)
- Fix u-p-c and u-p-h label/description assertions to match shortcuts.json
…NW3D11002Y35T)

The Producer Review entry in shortcuts.json had "key": ["r"] (array) instead
of "key": "r" (string), causing the validator to silently drop the entry.
The r key shortcut for toggling "Needs Producer Review" never fired.

Changes:
- shortcuts.json: ["r"] → "r" for the Producer Review entry
- shortcut-config.test.ts: Update total entry count from 20 to 21
- shortcut-config.test.ts: Add explicit test for r key lookup
…WIX001516P)

When a Pi model alias is selected but the resolved provider/model has not
yet arrived from the proxy response, the footer line 3 now displays
'{alias} → (resolving)' instead of just the bare alias.

This closes the visual gap between model selection and resolved model
arrival, giving users immediate feedback that the model is being resolved.

Changes:
- session-health.ts: Updated model portion rendering to show
  '{alias} → (resolving)' when selectedModel is set but resolvedModel is null
- session-health.test.ts: Updated test to expect new resolving state display

All 118 relevant tests (session-health: 91, model-display: 27) pass.
…eful shutdown (SA-0MRO7C2S4002Y5DD)

Implement three features to fix orphaned mock-git processes:

1. Mock Script Timeout Guard
   - Add SECONDS-based wall-clock timeout to mock-bin/git and mock-bin/gh
   - Add iteration counter cap (100) to rev-parse directory-walking loops
   - Add setTimeout-based timeout to mock-bin/wl (Node.js)
   - Configure via WORKLOG_MOCK_TIMEOUT env var (default 5s)
   - Update mock-bin/README.md with timeout documentation

2. Process Lifecycle Tracking in Test Helpers
   - Add pidTrackingSet and killTrackedProcesses() to cli-helpers.ts
   - Track child PIDs in execAsync and execWithInput
   - Auto-remove PID from tracking set on process exit
   - Wrap exec in _execTracked for PID tracking

3. Graceful Shutdown for Test Suite
   - Install signal handlers (SIGTERM, SIGINT, SIGHUP, beforeExit) in
     cli-helpers.ts at module scope
   - Install complementary handlers in setup-tests.ts

New test files:
- tests/cli/mock-timeout.test.ts (8 tests)
- tests/cli/process-lifecycle.test.ts (6 tests)
- tests/cli/process-cleanup-integration.test.ts (5 tests)

All 19 new tests pass. Full CLI test suite (52 files, 435 tests) passes.
Sorra the Orc and others added 28 commits July 21, 2026 02:43
- Add killProcessesForWorktree(worktreePath) call in the finally block
  of withTempWorktree(), before git worktree remove --force
- Import killProcessesForWorktree from process-lifecycle module
- Best-effort cleanup wrapped in try/catch to preserve existing behavior
- 7 new tests covering cleanup integration, no-op case, concurrent kills,
  and end-to-end spawn-track-kill lifecycle

All 55 tests pass (32 original + 16 auto-register + 7 new).
- New command: wl cleanup-worktree <path> [--force]
- wl cleanup-worktree --all [--force] to kill all tracked processes
- Uses process-lifecycle module's killProcessesForWorktree/killAllTracked
- SIGTERM by default, SIGKILL with --force
- No-op when no processes are tracked (exit 0)
- JSON output support via --json
- Registered in cli.ts built-in commands
- 9 new tests covering: single path, --all, --force, no-op, scoped cleanup,
  integrated with auto-registered processes

All 64 tests pass. TypeScript compiles cleanly.
- Create docs/PROCESS_LIFECYCLE.md with:
  - Overview and architecture of the process lifecycle module
  - Data flow diagram (ASCII)
  - Key concepts: process group killing, worktree context, watchdog
  - Complete API reference for all exported functions
  - CLI integration details for wl cleanup-worktree
  - withTempWorktree integration explanation
  - Error handling table
  - Testing documentation

Code comments are already adequate in src/process-lifecycle.ts and src/sync.ts.
The wl cleanup-worktree --help text is auto-generated by commander and clear.
…ly (WL-0MRO7LWIX001516P)

The ticker function refreshState() only called requestRender() when
initialPrompt was first set. After that, state updates (model resolution,
token counts, context usage) happened in memory but weren't reflected in
the UI until an event fires, causing the model status to appear stuck.

Fix: Moved the requestRender() call outside the if (!state.initialPrompt)
block so the footer re-renders every tick (1s) with the latest state.

Addresses audit rejection: 'the status does not seem to update as quickly
as i would expect.'

Changes:
- session-health.ts: Keep in-block requestRender for initialPrompt,
  add unconditional requestRender at end of each tick
- Remove duplicate createMockContext function (lines 263-290)
- Change vi.clearAllMocks() to vi.resetAllMocks() in work item
  ID resolution describe block beforeEach

Root cause: vi.clearAllMocks() only resets call data but preserves
mock implementations/return values. Combined with a duplicate function
definition, mock state could leak between tests when run in the full
suite. vi.resetAllMocks() clears both call data AND implementations,
ensuring each test starts with a completely clean slate.
- Remove duplicate createMockContext function (lines 263-290)
- Change vi.clearAllMocks() to vi.resetAllMocks() in work item
  ID resolution describe block beforeEach

Root cause: vi.clearAllMocks() only resets call data but preserves
mock implementations/return values. Combined with a duplicate function
definition, mock state could leak between tests when run in the full
suite. vi.resetAllMocks() clears both call data AND implementations,
ensuring each test starts with a completely clean slate.
…date README

- Add test for AC4: when resolvedModel is available but no selectedModel
  alias, displays just the resolved model (e.g., 'openai/gpt-4')
- Update README to document the (resolving) intermediate state on Line 3
- Fix outdated layout diagram in README to show three-line footer

All 3325 tests pass (175 files).
… Python tests

Add a tests/conftest.py that automatically adds /home/rgardler/.pi/agent/skills
to sys.path at collection time. This fixes the pre-existing test suite failure
where 'python3 -m pytest tests/' failed to collect test_audit_runner_core.py
because the 'audit' module was not on the path.

Closes: WL-0MRWRLJNV000T1E8
Add a 'Heartbeat Skill' feature bullet and a documentation table entry
pointing to skill/heartbeat/SKILL.md. Users can now discover the
/skill:heartbeat invocation and what it does from the README.

Closes: WL-0MRWRL35R0039RS5
Add 9 integration tests in test_heartbeat_integration.py that exercise real
wl CLI commands against an isolated temporary worklog database:

- 3 tests for real wl command output format (list, next, audit-show)
- 1 test for sparse queue flagging via real wl
- 1 test for full queue audit via real wl
- 1 test for all-items-ready reporting
- 1 test for temp directory isolation verification
- 2 tests for heartbeat.run_wl() subprocess behavior

These complement the 19 existing unit tests which mock wl/subprocess calls.
All 28 heartbeat tests pass (19 unit + 9 integration).

Closes: WL-0MRWRKVWM003YVAY
When a user explicitly runs /skill:heartbeat, proceed immediately
without checking conversation history for completion signals. This is
equivalent to --force — the user is taking explicit control.

Agent-initiated invocations (autonomous idle loops, cron-like contexts)
still perform the gate check to avoid interrupting mid-process tasks.

Ref: WL-0MRIHB4Q100946QD
The heartbeat skill's get_audit_result() now checks rawOutput first,
then falls back to summary. This allows heartbeat to read audits that
were stored with rawOutput: null (content in summary instead).

This is a defensive fix that complements the audit_runner.py readback
verification fix (SA-0MRY2BKEA008XCWD).

Refs: SA-0MRY2BKEA008XCWD
…oss sessions

When a new Pi session starts, the model-display module now resets the
resolved provider/model from the previous session. This ensures the
footer correctly shows '{alias} -> (resolving)' until the new session's
proxy response arrives, rather than displaying the stale resolved model
from the prior session.

- model-display.ts: session_start handler resets _resolvedModel to null
- model-display.test.ts: test verifies reset behavior on session start

Closes WL-0MRZ1EIY5008VBE6
When buildAuditEntry writes to the audit_results table, preserve any
existing rawOutput (e.g. from wl audit-set) so that --audit-text
doesn't clobber the machine-readable audit payload.

Co-authored-by: pi <pi@earendil.works>
…se=true

Add auditStaleIcon(), auditStaleLabel(), auditStaleFallback() to
src/icons.ts following the existing icon module pattern.

When an in_review item has a stale audit but passed (auditResult === true),
show the green square button (🟩, U+1F7E9) with fallback [YES_STALE]
instead of falling back to the stage icon (🔍).

This preserves the information that audit passed even after minor updates
make the audit timestamp appear stale.

Closes WL-0MS1OJZL7005CR7A
…tion

Also fixes test failure for validator.test.ts
Also rebuilds dist/icons.js to include 🟩 stale-passed icon
… init

- Fix getVersion() in ESM: use createRequire instead of bare require()
- Fix generate-version.cjs: patch dist/version.js, not the placeholder (removed)
- Add Worklog init step to CI workflows before running tests/smoke
- Rebuild dist with correct v1.0.4 version
…IFE, add build step for tui-tests

- Remove broken tui-tests job (referencing deleted files from old Blessed TUI)
- Add build and init steps to cli-tests job
- Fix smoke test tsx -e top-level await issue with async IIFE
…em for show test

- skill-path-conventions: skip gracefully when ~/.pi/agent/skills/ not available
- headless-tui: create work item before testing wl show instead of using hardcoded ID
@SorraTheOrc
SorraTheOrc merged commit b31cac2 into main Jul 26, 2026
4 checks passed
@SorraTheOrc
SorraTheOrc deleted the release/dev-to-main-20260726135209 branch July 26, 2026 15:23
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