Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
42ad5f9
WL-0MTV67MZU003H7SH: Implementation complete
Oct 4, 2026
779a578
OSL-0MUTPC7SF0011Y54: normalize worklog-root-relative podcast Key Fil…
SorraTheOrc Oct 4, 2026
f16e759
WL-0MU2EOHK900425VU: Implementation complete
Oct 4, 2026
a22442d
WL-0MUNMD0BE001TBL7: Implementation complete
Oct 4, 2026
1887756
WL-0MUNS8X97007C9H9: Implementation complete
Oct 4, 2026
0a45de0
WL-0MUKYEXMK0033MFK: Implementation complete
Oct 4, 2026
84d714b
OSL-0MUU1HBAE000KWHC: wl doctor check for resolvable podcast Key File…
Oct 4, 2026
1db87a5
WL-0MUUAMP7M008Z4GK: Implementation complete
Oct 4, 2026
03d0a50
WL-0MUH7ACKJ0024VGF: Implementation complete
Oct 4, 2026
eaa4d68
WL-0MUV0VEFW007ZBB7: Implementation complete
Oct 5, 2026
f57b90d
WL-0MUV4B3QZ007THLW: Implementation complete
Oct 5, 2026
386733c
WL-0MUNMCZOX003VXTE: Implementation complete
Oct 5, 2026
d9ea210
WL-0MUNMD0XY006BTE7: Implementation complete
Oct 5, 2026
ed0acc6
CG-0MUUFGXFX004O2X7: Serialise mutating commands on store lock (WL-0M…
Oct 5, 2026
6531a5b
feat(recovery): retry finish_reason: error in SERVER_ERROR category (…
Oct 6, 2026
3473421
feat: replace single-key c shortcut with c-n/c-q chords (WL-0MTCLB50D…
Oct 6, 2026
0314928
WL-0MUW3ODN00084XP0: Implementation complete
Oct 6, 2026
762f1fa
WL-0MTGDZVLV0055T4C: fix duplicate not-found error in wl show
Oct 6, 2026
3c7e888
WL-0MUBEDFLC004JN86: Implementation complete
Oct 6, 2026
9879fd4
WL-0MUWCIGNB0025SG6: Implementation complete
Oct 6, 2026
5c0c3aa
WL-0MUW2IFTE001ZL1A: Implementation complete
Oct 6, 2026
4423940
WL-0MTCLB50D0026YA7: Implementation complete
Oct 6, 2026
cf2c69b
WL-0MUMCZAC40058HCC: Implementation complete
Oct 6, 2026
e09a447
WL-0MUWIHUL2000JEFR: Implementation complete
Oct 6, 2026
55b085a
WL-0MUWCGF670087GAS: Add typed cta config field, resolver, docs, test…
Oct 6, 2026
512f33c
Merge branch 'fix/OSL-0MUU1HBAE000KWHC-podcast-key-files-doctor' into…
Oct 6, 2026
8983702
Bump version to v1.0.19
Oct 6, 2026
55ca14c
Update CHANGELOG.md for v1.0.19
Oct 6, 2026
ebe4fc0
Merge origin/dev into main (automated)
Oct 6, 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
4 changes: 4 additions & 0 deletions .worklog/config.defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ statusStageCompatibility:
blocked: [idea, intake_complete, plan_complete]
completed: [in_review, done]
deleted: [idea, intake_complete, plan_complete, done]
# Optional per-project call-to-action (CTA) surfaced in release/report output.
# Free-form Markdown — quote the value so link syntax survives the YAML
# round-trip. Absent = no behaviour change. See CONFIG.md for details.
# cta: "[Play the alpha release](https://example.github.io/my-project/). Provide feedback in [Discord](https://discord.gg/gUKQTFkzQ4)"
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Changelog

## v1.0.19 (2026-10-06)
> **Release focus:** This release focuses on reliability and workflow polish across task handling, sessions, and audits, fixing issues like endless retries, lost progress from connection errors, and sync conflicts that dropped completed work. It also improves visibility and speed with priority icons, clearer flag explanations, quick-create options, and more accurate reporting from the latest worklog data.
### Features
- Flagged items now explain why they need review, not just how to clear the flag. (WL-0MUUAMP7M008Z4GK)
- Installing in a worktree no longer breaks your global Pi extension. (WL-0MUSTNEQW003V3KO)
- Refactor and standup now use the latest worklog data, so reports and findings stay accurate. (WL-0MUAD8U24001ZK5K)
- Stops stuck tasks from retrying endlessly and flags them for review. (WL-0MUKYEXMK0033MFK)
- Interview now finds questions even when they're written in plain prose. (WL-0MUH7ACKJ0024VGF)
- Release notes now include a clear link to play the latest build and share feedback. (WL-0MUWCGF670087GAS)
- Spot urgent work at a glance with a new priority icon in the list. (WL-0MTQYTA20009YXBT)
- Active sessions now get priority for freed slots before new work starts. (WL-0MUV0VEFW007ZBB7)
- Create tasks faster with new quick-create options. (WL-0MTCLB50D0026YA7)
### Bug Fixes
- Completed work and comments no longer get lost when multiple sessions sync at once. (WL-0MUV2U9QF002S9J1)
- The finish step now runs your project's full test suite, so pushes are properly validated. (WL-0MUL6LCE00042MB0)
- Downtime work now only starts on items visible in your sprint view, so nothing hidden gets picked up. (WL-0MUNS8X97007C9H9)
- Transient connection errors now retry automatically instead of losing your progress. (WL-0MUQO8AEE003G1Z2)
- Panes no longer close while you're still working or waiting to respond. (WL-0MUMCZAC40058HCC)
- Passed audits no longer appear stale after minor edits, so you won't see false warnings or rerun audits. (WL-0MUN7QWFP0010EQC)
- Audits no longer crash on large codebases, so your results save reliably. (WL-0MSS55LFU00973S2)
- Downtime tabs no longer show an empty pane alongside your dispatch output. (WL-0MU2EOHK900425VU)
- Fixed duplicate error messages when viewing a non-existent wishlist. (WL-0MTGDZVLV0055T4C)
- Background updates now pause while you type, so no more dropped keystrokes. (WL-0MTV67MZU003H7SH)
### Other
- Longer explanations can now be shown without being cut off. (WL-0MUV4B3QZ007THLW)

## v1.0.18 (2026-10-04)
### Features
- Recent dispatches are now grouped by time of day, so you can see when work happened. (WL-0MUMM9NED009TLL3)
Expand Down
117 changes: 104 additions & 13 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,18 @@ wl --json list -s open --tags backlog
wl list --needs-producer-review
```

When `--json` is used, each work item includes the audit enrichment fields
`auditResult`, `auditedAt`, and `fingerprint` (from the `audit_results`
table), plus `currentFingerprint` — the canonical content fingerprint of the
item's *current* content (per-touched-path git state + description hash + Key
Files, matching the audit skill's algorithm). `currentFingerprint` is
computed only for items that carry a stored `fingerprint`; it is `null`
otherwise, or when git cannot determine it. Comparing `fingerprint` with
`currentFingerprint` is the primary `isAuditFresh` gate, so a fresh audit
stays fresh across `updatedAt` churn (WL-0MUN7QWFP0010EQC). Like the other
enrichment fields, `currentFingerprint` is omitted when an explicit `--fields`
projection is active.

---

### `search` <query> [options]
Expand Down Expand Up @@ -1253,18 +1265,78 @@ Answers are persisted after each response, so an interrupted session can be
resumed by re-running the command: only the questions that remain outstanding
are asked. `needsProducerReview` is cleared once every question has an answer.

#### LLM-assisted question extraction

The deterministic parser needs a recognised appendix heading and readable
Q/A markup. When it finds nothing — the item was written in free prose, for
example — the command can fall back to the local LLM to extract the
outstanding questions:

```sh
wl interview WL-ABC123 --llm
```

The opt-in can also be set in the configuration file:

```yaml
interview:
intelligent: true
```

`--llm` and `interview.intelligent: true` are equivalent; `--llm` wins when
both are present. `--no-llm` disables all LLM use, including extraction.

When enabled — and only when the deterministic parser found no questions —
the command sends the description (truncated to 8 KiB) to the configured chat
provider with a 15 s timeout and asks for a JSON array of
`[{"question": "..."}]`. Each returned question is shown in the same prompt
loop as a parsed question, and **only questions the operator answers
(confirms)** are written back — through the deterministic re-serialiser, never
from raw model output. When the description has no clarifying section the
canonical `## Appendix: Clarifying questions` heading is inserted; when one
exists the confirmed questions are merged into it, keeping re-runs idempotent.

If no provider is configured, or the request fails, times out or returns
malformed JSON, the command degrades silently to the deterministic-only
behaviour described below — no error is surfaced.

#### When there are no questions to answer

If no clarifying-questions section is found (or it contains no parseable
questions) but the item is still flagged for producer review, the command
explains what the producer must do to clear the flag. The explanation is
produced by the local LLM proxy's `compact` model, prompted with the
instruction *"Explain what the producer needs to do in order to remove the
needsProducerReview flag"* plus item context (the description and the two most
recent comments). When the LLM is disabled or unreachable the command falls
back silently to the two most recent comments (`<author>: <first line>`).
explains **why** the item is flagged — naming the triggering evidence — and
only then what the producer must do to clear the flag.

The explanation is advisory only — it never mutates the description.
The explanation is produced by the local LLM proxy's `compact` model, prompted
with an instruction to first state the reason (citing the triggering evidence)
and then the action, plus item context:

- the **latest audit result** (verdict, provenance, summary and a bounded
raw-output excerpt) — the strongest signal for review-gated items;
- the description; and
- the two most recent comments.

The audit block is placed ahead of the description so it survives the overall
context bound (`MAX_PROMPT_CONTEXT_BYTES`, 8 KiB) when the description is very
large. The summary and raw-output excerpt are individually bounded so a single
large audit cannot dominate the prompt.

When the LLM is disabled (`--no-llm`) or unreachable the command falls back
silently to the **structured evidence**, in priority order:

1. the latest audit result (verdict and summary);
2. a durable audit-gap waiver (`auditWaiver`);
3. the two most recent comments (`<author>: <first line>`);
4. a generic actionable line (`wl reviewed <id> false`) as a last resort.

The rendered explanation preserves intentional line breaks and short Markdown
structure — it is no longer collapsed into a single line — but collapses runs
of spaces and repeated blank lines, and is bounded by a documented maximum
(`MAX_EXPLANATION_LENGTH`, 8192 characters, and `MAX_EXPLANATION_LINES`, 512
lines). Truncation is marked with a trailing ellipsis.

The explanation is advisory only — it never mutates the description or clears
the flag.

After printing the explanation the command asks
`Clear the needsProducerReview flag? (y/N)` (default **No**); answering `y`
Expand All @@ -1279,12 +1351,18 @@ Options:
- `--prefix <prefix>` — Operate on a specific prefix (optional).
- `--json` — Non-interactive mode: prints JSON with
`needsProducerReview` (boolean), `producerReviewExplanation` (string or
`null`), `noSection`, `noQuestions`, `total`, `outstanding` and
`allAnswered`. Performs no prompts, no mutation and (with `--no-llm`) no
LLM call.
- `--no-llm` — Disable the LLM explanation and use the comment fallback
(default: LLM on).
- `--model <model>` — Override the chat model used for the explanation.
`null`; multi-line explanations are preserved), `noSection`,
`noQuestions`, `total`, `outstanding` and `allAnswered`. Performs no
prompts, no mutation and (with `--no-llm`) no LLM call.
- `--no-llm` — Disable all LLM use (the producer-review explanation and the
LLM-assisted question extraction) and use the structured fallback instead
(default: explanation on, extraction off unless enabled).
- `--llm` — Enable LLM-assisted clarifying-question extraction when the
deterministic parser finds no questions. Equivalent to
`interview.intelligent: true`; the flag wins when both are set (default:
off).
- `--model <model>` — Override the chat model used for the explanation and
extraction.

> Without `--json`, the command requires interactive (TTY) input.

Expand Down Expand Up @@ -1314,6 +1392,19 @@ Defaults: `baseUrl` `http://192.168.0.199:8000/v1`, `model` `compact`,
`LLM_API_KEY` and `LLM_TIMEOUT_MS` are used as fallbacks; config values take
precedence over environment variables.

#### `interview` configuration

The `interview` section opts in to LLM-assisted question extraction without
requiring the `--llm` flag:

```yaml
interview:
intelligent: true
```

An absent section (or `intelligent: false`) leaves extraction off. The
provider itself is configured under `llm` (see above).

### `help` [command]

Show help for a specific command.
Expand Down
32 changes: 32 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,38 @@ The system loads configuration in this order:

If no configuration exists at all, the system defaults to using `WI` as the prefix.

## Project CTA (Call-to-Action)

The optional `cta` field declares a project-specific call-to-action — a
free-form **Markdown** string that report consumers (for example the ship
skill's CHANGELOG section and release notifications) can surface. It is
typically used by game projects to invite players to try a build and give
feedback.

```yaml
# .worklog/config.yaml
cta: "[Play the alpha release](https://example.github.io/my-project/). Provide feedback in [Discord](https://discord.gg/gUKQTFkzQ4)"
```

- **Optional.** When absent, the resolver returns `null` and report output is
unchanged (no behaviour change).
- **Markdown / YAML-safe.** The value is a Markdown string; **quote it** in
YAML (as above) so link syntax such as `[label](url)` survives the
round-trip. A present non-string value fails config validation.
- **Two-tier override.** Like every other field, a `cta` in
`.worklog/config.yaml` overrides a `cta` in `.worklog/config.defaults.yaml`.

Consumers read the merged value through the exported `resolveProjectCta()`
resolver in `src/config.ts`, or directly from the loaded `WorklogConfig`
(`config.cta`):

```ts
import { loadConfig, resolveProjectCta } from './config.js';

const config = loadConfig();
const cta = resolveProjectCta(config); // string | null
```

## GitHub Settings

Optional GitHub settings (edit `.worklog/config.yaml` manually):
Expand Down
17 changes: 17 additions & 0 deletions DATA_SYNCING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,23 @@ To prevent this, auto-sync spawners pass `wl sync --if-idle`:

Manual `wl sync` (without `--if-idle`) keeps the original behavior: it waits for the lock (with exponential backoff) and runs to completion.

### Concurrent Writes: Sync vs Mutating Commands

`wl sync` holds the per-store advisory file lock (`.worklog/worklog-data.jsonl.lock`, via `withFileLock` / `getLockPathForJsonl`) for its entire fetch → merge → write → push operation. The single-command mutators `wl update`, `wl comment add`, and `wl audit-set` acquire the **same** lock around their own read-modify-write, so a mutation and a sync can never interleave:

- If a mutation is in flight, `wl sync` waits for the lock instead of taking its snapshot while a write is landing.
- If a sync is in flight, a mutating command waits (bounded by the lock timeout, 30 s by default) instead of writing into the sync's read→write window.

This closes a lost-update race (WL-0MUV2U9QF002S9J1) in which a sync's stale snapshot overwrote a concurrent terminal `completed`/`in_review` transition and dropped a comment added in the window. The guarantee is **no lost update**: a mutation that commits after the sync's local read is never overwritten by the sync's write-back.

What is deliberately unchanged:

- **Intentional reopen / auto-revert still works.** A newer local `open` (including the `wl audit-set --ready-to-close no` auto-revert to `open`/`plan_complete`) still beats an older remote `completed`/`in_review` — the lock serialises the writes, it does not block the transition.
- **`wl sync --if-idle` still skips instead of queueing** when the lock is held (see above).
- **Comments remain add-only** in the merge layer.

Regression coverage: `tests/cli/sync-vs-mutation.test.ts` parks a `wl sync` inside `git fetch` and asserts that a concurrent terminal-state update and comment both survive.

### Config Options

Set in `.worklog/config.yaml` (local) or `.worklog/config.defaults.yaml` (team defaults):
Expand Down
1 change: 1 addition & 0 deletions DOCTOR_AND_MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This document describes the `wl doctor` command and the migration policy for Wor

- **Status/stage compatibility** — validates every work item's status and stage against the rules defined in `.worklog/config.yaml` (see `docs/validation/status-stage-inventory.md` for the full rule set).
- **Dependency edges** — checks that all dependency edges reference existing work items.
- **Podcast script `Key Files:`** — flags a `podcast` item at a script-bearing stage (`plan_complete` / `in_review` / `done`) whose `Key Files:` contains no `.podcast.md` path that resolves to an existing file under the worklog root, and any `podcast` item whose listed `.podcast.md` path does not resolve. Detection is artifact-based (filesystem), not stage/status-based. `wl doctor --fix` backfills the path when a matching script is found (see `docs/FILE_PATH_CONVENTION.md`).
- **Pending migrations** — the `upgrade` subcommand detects and applies schema migrations.
- **Stale deleted items** — the `prune` subcommand removes soft-deleted items older than a configurable threshold.
- **Audit gaps** — the `audit-gaps` subcommand reports `completed`/`in_review` items with no audit record (read-only).
Expand Down
4 changes: 3 additions & 1 deletion LOCAL_LLM.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ Defaults: `baseUrl` `http://192.168.0.199:8000/v1`, `model` `compact`,
`LLM_API_KEY` and `LLM_TIMEOUT_MS` are used as fallbacks; config values take
precedence over environment variables (matching the `embedding.*` precedence).
Every call is bounded by the timeout and degrades silently to a non-LLM
fallback on error. See [CLI.md](CLI.md#interview-options-id) for the
fallback on error; the fallback reports the flag reason from the structured
evidence first (the persisted audit verdict and summary, then an audit waiver,
then the review comments). See [CLI.md](CLI.md#interview-options-id) for the
`wl interview --no-llm` opt-out.

---
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ Install the extension globally:
npm run install:pi-extension
```

> **Installer guards.** `scripts/install-pi-extension.sh` only points the global
> symlink at a checkout it can actually load. It is a safe no-op (exit `0`, no
> mutation of `~/.pi/agent/extensions`) when either of the following holds:
>
> - **Worktree context** — `REPO_ROOT` contains the `.worklog/worktrees/` path
> segment (as created by `/skill:implement`). This prevents an unbuilt,
> short-lived worktree from repointing — and then breaking — the machine-global
> extension once the worktree is removed.
> - **Unbuilt checkout** — `dist/wl-integration/spawn.js` is absent. The
> extension resolves this compiled output at runtime, so installing from an
> unbuilt checkout would produce a broken extension. Run `npm run build`
> first.
>
> Because the guards exit `0` rather than failing, `npm install` in a worktree
> still succeeds and leaves the existing global symlink untouched.

For a single-command install of all integrations (herdr plugin + Pi extension):

```bash
Expand Down
12 changes: 12 additions & 0 deletions docs/AUDIT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,18 @@ Fingerprint sources: `wl audit-set --fingerprint <hex>`, or an
line. An explicit `--fingerprint`/`--audit-fingerprint` flag wins over an
embedded line.

**Current fingerprint at read time.** `wl list --json` emits
`currentFingerprint` for every item: the canonical fingerprint recomputed from
the item's current content (per-touched-path git state + description hash + Key
Files) via a TypeScript port of the audit skill's algorithm. It is computed for
items that carry a stored `fingerprint` and is `null` otherwise (or when git
cannot determine it). Consumers that only have `wl list` output — the herdr
selection list, the TUI extension, the review-queue banner — can therefore take
the primary content gate instead of the 60 s time gate
(WL-0MUN7QWFP0010EQC). The port is parity-tested against
`audit_runner._compute_content_fingerprint` (skipped when the audit skill is not
installed).

Flag-only flips of `needsProducerReview` do not bump `updatedAt`
(WL-0MSN6ZCTN0027U2R), and neither do comment writes (WL-0MUBVH6JM0093KVM):
comment create/update/delete bump `activityAt` only. `updatedAt` therefore
Expand Down
3 changes: 3 additions & 0 deletions docs/FILE_PATH_CONVENTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ the extraction logic. It is shared between:

- The `wl next --groups/-g` grouping algorithm
- The `wl doctor file-paths` subcommand
- The `wl doctor` podcast-script check (`src/doctor/podcast-scripts-check.ts`),
which additionally verifies that a podcast item's `.podcast.md` `Key Files:`
entry resolves to an existing file (see [DOCTOR_AND_MIGRATIONS.md](../DOCTOR_AND_MIGRATIONS.md))
- The automatic advisory check on transition to `intake_complete`

## Related
Expand Down
7 changes: 6 additions & 1 deletion docs/design/incremental-sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,12 @@ the records changed since the last full snapshot — not unbounded history.

The incremental path runs entirely inside the existing sync flow, so it
inherits unchanged:
- the file lock (`withFileLock`, `getLockPathForJsonl`),
- the file lock (`withFileLock`, `getLockPathForJsonl`) — also acquired by the
mutating commands `wl update` / `wl comment add` / `wl audit-set` around
their read-modify-write, so a sync can never overwrite a mutation that
landed after its local read (no lost update; see
[DATA_SYNCING.md § Concurrent Writes](../DATA_SYNCING.md#concurrent-writes-sync-vs-mutating-commands) and
`tests/cli/sync-vs-mutation.test.ts`),
- the single-flight guard and `--if-idle` skip semantics (WL-0MSAB7ZUC004SK7E),
- the ephemeral JSONL pattern (SQLite → JSONL → push → delete).

Expand Down
Loading
Loading