Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ab239a8
versions
Jul 9, 2026
efda775
WL-0MRCTZZ82000X7TM: Fix hook safety - post-merge/rewrite path, add -…
Jul 9, 2026
0d61c59
WL-0MRDEM7OO005UB1H: Extend doctor upgrade to refresh installed hooks…
Jul 9, 2026
caa0725
WL-0MRDRZ32L00404D0: Implement Session Health Extension for Pi footer
Jul 10, 2026
de2ceec
WL-0MRDRZ32L00404D0: Fix infinite recursion crash in registerSessionH…
Jul 10, 2026
5a15a77
WL-0MRC3F30E008XJN3: Address audit gaps - include extension statuses …
Jul 10, 2026
355a37d
WL-0MRERDU1J000W1IS: Show time since last streaming chunk in session …
Jul 10, 2026
d58ba4b
WL-0MRF19R71004YJND: Add model/provider line to TUI status bar footer
Jul 10, 2026
c8e1b82
WL-0MRF19R71004YJND: Show Pi model alias + resolved provider/model on…
Jul 10, 2026
c518c2e
WL-0MRF19R71004YJND: Always show model line, use → arrow character
Jul 10, 2026
b6b2e46
WL-0MRF19R71004YJND: Capture Pi model alias from session context for …
Jul 10, 2026
4544764
WL-0MRF6JXMD003KHLQ: Add initial prompt preview to footer info line
Jul 10, 2026
ad9c0a1
WL-0MRF6JR7P005TJU9: Fix missing requestRender() in ticker refreshSta…
Jul 10, 2026
72442b9
WL-0MRFH8W4J007HIY5: Fix extractInitialPrompt to handle Pi's array-ba…
Jul 10, 2026
cf8f006
WL-0MRFH8W4J007HIY5: Add skill block detection to extractInitialPrompt
Jul 10, 2026
229b46a
WL-0MRFH8W4J007HIY5: Enrich prompt preview with work item titles and …
Jul 10, 2026
86354fb
WL-0MRFJGLI4009GLEE: Restructure session health footer layout
Jul 11, 2026
b904289
WL-0MRE6JDT3004OSTF: Proactively release proxy model leases on sessio…
Jul 11, 2026
3a21db6
WL-0MRE6JDT3004OSTF: Resolve merge conflict in version.ts (keep dev v…
Jul 11, 2026
277421c
WL-0MRFJGLI4009GLEE: Update README documentation to reflect new foote…
Jul 11, 2026
50ec7fc
WL-0MRFMOU4Q009GUWH: Fix pre-existing test failures across 6 test files
Jul 11, 2026
545a02a
Sync work items and comments
Jul 11, 2026
691975a
Revert "Sync work items and comments"
Jul 11, 2026
e7336e8
WL-0MRFOJFVA003S8IB: Compact footer status line with shortened skill …
Jul 11, 2026
8d345d3
Bump version to v1.0.3
Jul 11, 2026
43fa13a
Update CHANGELOG.md for v1.0.3
Jul 11, 2026
30e4864
Merge origin/dev into main (automated)
Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .githooks/post-checkout
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ else
echo "worklog: wl/worklog not found; skipping post-checkout sync" >&2
exit 0
fi
if "$WL" sync >/dev/null 2>&1; then
if "$WL" sync --git-branch refs/worklog/data >/dev/null 2>&1; then
:
else
echo "worklog: sync failed or not initialized; continuing" >&2
Expand Down
2 changes: 1 addition & 1 deletion .githooks/post-merge
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
# worklog:post-pull-hook:v1
# Wrapper that delegates to central Worklog post-pull script (committed hooks).
exec "/tmp/Worklog/.githooks/worklog-post-pull" "$@"
exec "$(dirname "$0")/worklog-post-pull" "$@"
2 changes: 1 addition & 1 deletion .githooks/post-rewrite
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
# worklog:post-pull-hook:v1
# Wrapper that delegates to central Worklog post-pull script (committed hooks).
exec "/tmp/Worklog/.githooks/worklog-post-pull" "$@"
exec "$(dirname "$0")/worklog-post-pull" "$@"
2 changes: 1 addition & 1 deletion .githooks/worklog-post-pull
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ else
echo "worklog: wl/worklog not found; skipping post-pull sync" >&2
exit 0
fi
if "$WL" sync >/dev/null 2>&1; then
if "$WL" sync --git-branch refs/worklog/data >/dev/null 2>&1; then
:
else
echo "worklog: sync failed or not initialized; continuing" >&2
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## v1.0.3 (2026-07-11)
### Features
- Extend doctor upgrade to refresh installed hooks from .githooks (WL-0MRDEM7OO005UB1H)
- Session Health Extension for Pi Footer (WL-0MRDRZ32L00404D0)
- proactively release leases (WL-0MRE6JDT3004OSTF)
- Show time since last streaming chunk in session health footer (WL-0MRERDU1J000W1IS)
- Add model/provider line to TUI status bar footer (WL-0MRF19R71004YJND)
- Show initial prompt preview in footer info line (WL-0MRF6JXMD003KHLQ)
- Restructure session health footer: elapsed time next to state, total session time in center (WL-0MRFJGLI4009GLEE)
- Compact footer status line: shorten skill indicator and truncate work item IDs (WL-0MRFOJFVA003S8IB)
- Show initial prompt preview in footer info line (WL-0MRF6JR7P005TJU9)
### Bug Fixes
- RCA: wl sync pre-push hook destroyed Tableau-Card-Engine repository (WL-0MRCTZZ82000X7TM)
- Fix extractInitialPrompt to handle Pi's array-based content format (WL-0MRFH8W4J007HIY5)
### Other
- Move provider/model display to start of Worklog extension status line (WL-0MRC91VIN0089UXD)
- Fix pre-existing test failures in unrelated test files (WL-0MRFMOU4Q009GUWH)
- Integrate model-display extension into the Worklog extension (WL-0MRC3F30E008XJN3)
- Change audit/review question mark icon from red to grey in selection list (WL-0MRCN2HF5003HSG9)
- Model display extension is not showing the provider (WL-0MRC3NOVZ000P0P5)

## v1.0.2 (2026-07-08)
### Other
- Integrate model-display extension into the Worklog extension (WL-0MRC3F30E008XJN3)
Expand Down
53 changes: 53 additions & 0 deletions docs/branch-protection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Branch Protection Rules

> **Operational/GitHub admin task.** These rules must be configured in GitHub repository settings.

## Background

On 2026-07-09, an incident (WL-0MRCTZZ82000X7TM) corrupted the `dev` branch of this repository when a pre-push hook executed a destructive `wl sync` that deleted all source files. The commit `b639581` rendered `origin/dev` unusable until a force-push from a feature branch restored it.

While the hooks have been fixed to use safe patterns, branch protection rules provide an additional safety layer that prevents any single commit (even from a trusted hook) from irreversibly damaging the repository state.

## Required Rules

### `main`

- **Require pull request reviews before merging**: ON
- **Dismiss stale pull request approvals when new commits are pushed**: ON
- **Require status checks to pass before merging**: ON
- All CI checks must pass
- **Require branches to be up to date before merging**: ON
- **Require linear history**: ON
- **Do not allow force pushes to the `main` branch**: ON
- **Do not allow deletions of the `main` branch**: ON

### `dev`

- **Require pull request reviews before merging**: ON
- **Dismiss stale pull request approvals when new commits are pushed**: ON
- **Require status checks to pass before merging**: ON
- All CI checks must pass
- **Require branches to be up to date before merging**: ON
- **Require linear history**: ON (if possible)
- **Do not allow force pushes to the `dev` branch**: ON (strongly recommended)
- **Do not allow deletions of the `dev` branch**: ON

## Rationale

- **No force pushes**: Prevents accidental or malicious overwriting of branch history. The `dev` branch corruption incident was only recoverable because a clean feature branch existed. Without force-push protection, similar incidents could permanently lose work.
- **PR reviews**: Ensures at least one human (or automated system) approves changes before they reach protected branches. This is especially important for hooks, automation scripts, and configuration changes.
- **Status checks**: Ensures CI passes before merging. This prevents broken code from reaching protected branches.
- **Linear history**: Makes it easier to reason about the commit history and reduces the risk of accidental merges introducing conflicts or regressions.

## Configuration

These rules are configured via GitHub's UI:

1. Go to **Settings > Branches > Branch protection rules**
2. Click **Add rule** or **Edit** for each branch
3. Configure the rules as described above

## Related

- Incident: WL-0MRCTZZ82000X7TM (RCA: wl sync pre-push hook destroyed repository)
- Hook fixes: Same work item — `.githooks/pre-push`, `.githooks/post-merge`, `.githooks/post-rewrite`, `.githooks/post-checkout`, `.githooks/worklog-post-pull`, `.git/hooks/pre-push`, `.git/hooks/worklog-post-pull`
43 changes: 43 additions & 0 deletions docs/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,46 @@ Audit field migration note
- Use `wl update --audit-text <text>` to set audit via the existing CLI interface (writes to `audit_results` table).
- Redaction/safety rules for audit text are tracked separately in `Redaction and Safety Rules for Audit Text (WL-0MMNCOIYS15A1YSI)`.
- See `docs/AUDIT_STATUS.md` for full documentation on audit status semantics.

Hook upgrades
-------------
The `wl doctor upgrade` command also checks and upgrades outdated git hooks installed in
`.git/hooks/` from the committed safe versions in `.githooks/`. This ensures existing
installations automatically get the safe hook patterns (using `--git-branch refs/worklog/data`)
without manual re-installation.

A hook is considered outdated when it:
- Lacks the safe `--git-branch refs/worklog/data` guard, or
- Contains hardcoded paths like `/tmp/Worklog/...` or absolute paths to `.git/hooks/` for
the central post-pull script (should use `$(dirname "$0")`), or
- Differs in any way from the committed `.githooks/` version.

Only hooks that contain a Worklog marker (`worklog:pre-push-hook:`, `worklog:post-pull-hook:`,
or `worklog:post-checkout-hook:`) are considered for upgrade. Non-Worklog hooks are skipped.

Examples:

- Preview outdated hooks alongside migrations (dry-run):

`wl doctor upgrade --dry-run`

- Preview outdated hooks with JSON output:

`wl doctor upgrade --dry-run --json`

- Apply outdated hooks alongside migrations (non-interactively):

`wl doctor upgrade --confirm`

- Apply outdated hooks alongside migrations (interactive prompt):

`wl doctor upgrade`

The committed hook scripts live in `.githooks/` and include:
- `pre-push` — auto-syncs Worklog data before pushing, uses `--git-branch refs/worklog/data`
- `post-checkout` — auto-syncs after branch checkout
- `post-merge` — wrapper that delegates to `worklog-post-pull`
- `post-rewrite` — wrapper that delegates to `worklog-post-pull`
- `worklog-post-pull` — central sync script

See `src/doctor/hook-upgrade.ts` for the implementation.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "worklog",
"version": "1.0.2",
"version": "1.0.3",
"description": "A simple experimental issue tracker for AI agents",
"main": "dist/index.js",
"type": "module",
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,9 @@ export class WorklogDatabase {
this.store.saveWorkItem(item);
this.store.upsertFtsEntry(item);
this.triggerSemanticIndex(item);
// Clear comment caches that triggerSemanticIndex may have populated
// with stale (empty) data, so subsequent reads see fresh results.
this.store.clearCommentCaches();
this.triggerAutoSync();
return item;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/shared/src/persistent-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ export class SqlitePersistentStore {
});

doUpdates();
this.invalidateWorkItemCaches();
return updated;
}

Expand Down Expand Up @@ -1686,6 +1687,7 @@ export class SqlitePersistentStore {
*/
private invalidateWorkItemCaches(): void {
this.cacheInvalidatePrefix('workitem_');
this.cacheInvalidatePrefix('commentsForItem_');
this.cacheInvalidate('allWorkItems');
this.cacheInvalidate('countWorkItems');
this.cacheInvalidate('allChildren');
Expand All @@ -1710,6 +1712,13 @@ export class SqlitePersistentStore {
this.cacheInvalidate('allDependencyEdges');
}

/**
* Public wrapper to clear comment-related caches.
*/
clearCommentCaches(): void {
this.invalidateCommentCaches();
}

close(): void {
this.db.close();
this.cacheClear();
Expand Down
139 changes: 139 additions & 0 deletions packages/tui/extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,109 @@ when used outside the Pi TUI.
- Built-in Pi commands and free-form text clear the indicator via the same
`input` event handler.

## Session Health Footer

The extension displays a **real-time session health footer** that replaces
Pi's default footer with a rich health dashboard showing:

### What It Displays

The footer uses a **three-section layout**: **left** (status + elapsed time since last response + turn count + last-chunk timer), **center** (total wall-clock session duration), and **right** (token usage + context + model).

| Element | Description |
|---------|-------------|
| **Status marker** | `○` idle, `●` streaming, `⚡ <tool>` tool execution |
| **Last chunk time** | Elapsed time since the last streaming chunk (e.g., `(Last Chunk: 3s ago)`) — shown only during active streaming |
| **Turn count** | Number of turns in the current session (e.g., `#3`) |
| **Response elapsed** | Colour-coded elapsed time since the last model response (shown after the state marker in the left section) |
| **Total session time** | Total wall-clock session duration (e.g., `Total: 5m 42s`) — shown in the center section |
| **Token usage** | Input/output token counts (e.g., `↑1.2k ↓4.5k`) |
| **Context usage** | Percentage of context window (e.g., `76.8%/128k`) |
| **Model ID** | Currently active model (e.g., `gpt-4`) |

### Colour Coding

The response age indicator uses colour coding to provide at-a-glance health:

| Colour | Threshold | Meaning |
|--------|-----------|--------|
| Green (`success`) | < 5s | Healthy — response received recently |
| Yellow/Orange (`warning`) | 5–30s | Moderate delay — model is processing |
| Red (`error`) | > 30s | Stuck or slow — consider interrupting |

### Layout

The footer spans two lines. The first line shows extension status entries
(e.g., resolved provider/model, activity indicator). The second line shows
session health metrics in a **three-section layout**:

```
openai/gpt-4 ⏵ /wl ← Extension statuses
● Streaming 45s #5 (3s ago) Total: 5m 42s ↑1.2k ↓4.5k 39.1%/128k ← Session health
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ └────────────── Context usage
│ │ │ │ │ │ │ └────────────────────── Output tokens
│ │ │ │ │ │ └─────────────────────────── Input tokens
│ │ │ │ │ └─────────────────────────────────────── Total session time
│ │ │ │ └───────────────────────────────────────────────────── Last chunk timer (streaming only)
│ │ │ └──────────────────────────────────────────────────────── Turn count
│ │ └──────────────────────────────────────────────────────────── Elapsed time since last response
└────────────────────────────────────────────────────────────────────────── Status marker
```

During **idle** or **tool execution**, the last-chunk timer is not shown.

### Event Tracking

The extension subscribes to the following Pi lifecycle events to update state:

| Event | Update |
|-------|--------|
| `turn_start` | Increment turn count, set status to streaming, set last-response timer |
| `message_update` | Update last-chunk timer (shown during active streaming) |
| `message_end` (assistant) | Set status to idle, update response time |
| `tool_execution_start` | Set status to tool with tool name |
| `tool_execution_end` | Reset status to idle |
| `model_select` | Refresh footer display |
| `session_start` | Reset counters, initialize footer |
| `session_shutdown` | Clean up ticker interval |

### Ticker

A 1-second `setInterval` ticker refreshes the token counts and context usage
from the session manager. The footer re-renders automatically when the branch
changes.

### Graceful Degradation

The session health footer gracefully degrades in non-TUI modes (print, JSON,
RPC) where `setFooter` is a no-op. The feature has no effect and does not
produce errors when used outside the Pi TUI.

### Technical Notes

- Uses Pi's `ctx.ui.setFooter()` API to replace the entire footer with a
custom render function.
- The footer renderer uses `truncateToWidth` and `visibleWidth` from
`@earendil-works/pi-tui` for safe ANSI-aware truncation.
- Only one `setFooter()` can be active at a time. This module's footer
replaces Pi's default footer (git branch, cwd path, etc.).
- The footer includes extension status entries (set via `ctx.ui.setStatus()`)
from `footerData.getExtensionStatuses()` as the first line. This ensures
that status entries from other modules — such as the resolved
provider/model (`worklog-0model`) and the activity indicator
(`worklog-activity`) — remain visible alongside the session health metrics.
- The footer uses a **three-section layout**: left (status marker + elapsed
time since last response + turn count + last-chunk timer), center (total
session duration), and right (token counts + context usage + model ID).
- A `lastChunkTime` property tracks the timestamp of the last `message_update`
event. The **last-chunk timer** `(Last Chunk: Xs ago)` is displayed in the
left section only when `status === 'streaming'`.
- Token counts are calculated from session entries by summing
`usage.input` and `usage.output` from assistant messages.
- Context usage is obtained from `ctx.getContextUsage()` which returns
`{ tokens, contextWindow, percent }`.

## Error Recovery Module

The extension includes a built-in automatic error recovery module that replaces the
Expand Down Expand Up @@ -589,4 +692,40 @@ Example:
"description": "Update the priority of the selected work item"
}
```

## Lease Release

The extension includes a **proactive lease release** module that automatically
releases the previous session's model lease when a new Pi session is created
(via `/new`). This speeds up model reclamation on the Local Proxy provider.

### How It Works

1. When Pi fires a `session_start` event with reason `"new"` (indicating a
session replacement), the module reads `~/.pi/agent/models.json` to locate
the `"Local Proxy"` provider's `baseUrl`.
2. It sends a best-effort `POST {baseUrl}/leases/release` with a JSON body
containing the previous session's identifier (`previousSessionFile`).
3. The call is **fire-and-forget**: it does not block session startup.
Failures (network errors, non-2xx responses) are silently logged at
debug level only — no user-visible errors.
4. If the `"Local Proxy"` provider is not configured, no request is sent.

### When It Fires

| Session Start Reason | Lease Release Triggered |
|----------------------|------------------------|
| `"startup"` (initial Pi launch) | No |
| `"new"` (session via `/new`) | Yes |
| `"resume"` (session resumed) | No |
| `"fork"` (session forked) | No |
| `"reload"` (extensions reloaded) | No |

### Technical Notes

- Implemented in `Worklog/lease-release.ts`.
- The proxy configuration is read at runtime from `~/.pi/agent/models.json`.
- Results are cached per-extension-lifecycle to avoid repeated filesystem reads.
- Registered in `Worklog/index.ts` via `registerLeaseRelease(pi)`.
- Tests are in `Worklog/lease-release.test.ts`.
```
5 changes: 3 additions & 2 deletions packages/tui/extensions/Worklog/activity-indicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

import type { ExtensionAPI, ExtensionContext, ExtensionUIContext } from '@earendil-works/pi-coding-agent';
import { runWl } from '../wl-integration.js';
import { truncateWorkItemId } from './terminal-utils.js';

/**
* Status key used for the activity indicator in the footer.
Expand Down Expand Up @@ -237,10 +238,10 @@ async function showActivityWithTitleLookup(ctx: StatusContext, text: string, sho
const title = await resolveWorkItemTitle(id);
if (!title) return;

// Replace with command + ID + title format, truncated to fit terminal width.
// Replace with command + truncated ID + title format, truncated to fit terminal width.
// The command is formatted via formatCommandContext (e.g., /skill:audit → audit).
const commandCtx = formatCommandContext(text);
const display = `${commandCtx} ${id} ${title}`;
const display = `${commandCtx} ${truncateWorkItemId(id)} ${title}`;
showActivity(ctx, display, showIndicator);
}

Expand Down
Loading
Loading