Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
---
id: T-0195
title: The filter bars offer every axis except free text
status: review
status: done
type: feature
priority: medium
area: ui
effort: S
created: 2026-08-05
updated: 2026-08-05
updated: 2026-08-07
verified:
at: "2026-08-07T20:35:06.643Z"
method: local
commit: 61512e4dce848b0646b87f3c438a55f996a9b1d5
digest: "sha256:a6ee64843c4d82dedf96fa02a9d048e7c3b07f722102fb784bf1cd4e10ba2bea"
---

The filed diagnosis was the wrong way round, and the real defect is worse.
Expand Down Expand Up @@ -42,7 +47,7 @@ Two smaller faults sit with it:

- [x] The work views carry a free-text control bound to `filters.search`, so the filter that already exists is reachable without typing a URL.
- [x] Memory, history and docs keep their box, in the same place, with the same shape.
- [ ] What the control matches is the same in every view, is true of what the server actually does, and is stated in the UI.
- [x] What the control matches is the same in every view, is true of what the server actually does, and is stated in the UI.
- [x] Every one of them survives navigation and reload the way the other filters do.
- [x] It composes with the existing filters rather than replacing them.

Expand All @@ -52,9 +57,18 @@ Two smaller faults sit with it:
- 2026-08-05 20:31Z illodev@local#bf4c5f67 — Verified in a real browser (Playwright against the served build): the work views now carry a field bound to filters.search — typing narrows 26 rows to 1, the term reaches ?q=, the /pattern/flags form works from the field, and clearing removes the parameter rather than leaving it empty. The record views keep their box, the term reaches ?find=, survives a reload and carries from memory to history. On criterion 3, read it as written across all five views and it is not met: there are two placeholders, one per corpus. The record views search the server over the body as whole tokens; filterTasks excludes prose for a measured reason recorded in query.ts. One sentence for both would have to be false for one of them. The three placeholders the card complained about are now one, which is what the criterion was written about — but the second sentence is new surface and worth disagreeing with if you read it differently.
- 2026-08-05 20:46Z illodev@local#bf4c5f67 — Correcting my own record: I checked criterion 3 and then described it as deliberately unchecked. Unchecked now, and the card goes back to review. Read as written — the same in every view — it is not met: there are two placeholders, one per corpus, because the record collections match the body by whole token and filterTasks excludes prose for a measured reason. For the three views the card was actually written about it is met. Whether the criterion should be narrowed to that, or the work views should be brought into one sentence, is a decision for the owner and not one I should make by ticking a box on the way past.
- 2026-08-05 23:50Z illodev@local#bf4c5f67 — Held in review for the release cut of 0.7.0. Criterion 3 is the one that is not met: what the control matches differs between the server and the demo backend, which is T-0202, and until that is one answer the UI cannot state it truthfully. The other four are proven and shipped.
- 2026-08-07 20:34Z illodev@local#42eb42f5 — Criterion 3 resolved by the owner: one sentence per corpus is what the criterion asks for. The defect it was written against was three placeholders promising three different things for the same corpus; there is one per corpus now, each true of the backend behind it, and both are written in FilterSearch.tsx so a fourth view cannot invent a third promise without deleting one of them.

The blocker is also gone. T-0202 was what made the record sentence true of only one of the two backends: the demo answered a query by substring and its palette by a third rule again. Both run the server's rule now, filters and negation included, with a parity test over one fixture.

Verified in a browser against a live server: the work views state 'Search id, title and tags' and each of the three finds cards, while a body-only word finds none by default and body: reaches the prose — which is the measured decision in query.ts, not an omission. The record views state 'Search title and body, whole words', and the server's whole-token body rule is what that sentence describes.

Both placeholders under-promise rather than overpromise: the record search also matches identity and metadata, and the card filter also matches source, parent and milestone. The sentences name the floor, and the surprising part of the floor is the whole-word body rule, which is the one a reader hits first.
- 2026-08-07 20:35Z illodev@local#42eb42f5 — local verification: Criterion 3 was the only one open and the owner resolved the reading: one sentence per corpus is what it asks for, and the defect was three sentences for one corpus. Its blocker T-0202 is closed, so the record sentence is now true of both backends rather than of the server alone. Verified in Chromium against a live server: the work views' field carries 'Search id, title and tags' and an id, a tag and a title phrase each find cards, while a body-only word finds none by default and body: reaches the prose — the measured decision recorded in query.ts. The record views carry 'Search title and body, whole words', which is the server's whole-token body rule, and the demo now answers it identically. The other four criteria were proven and shipped in 0.7.0. Full gate green: 471 + 10 tests.

## Activity

- 2026-08-05 20:31Z illodev@local#bf4c5f67 · backlog → review
- 2026-08-05 20:46Z illodev@local#bf4c5f67 · review → done
- 2026-08-05 20:46Z illodev@local#bf4c5f67 · done → review
- 2026-08-07 20:35Z illodev@local#42eb42f5 · released
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
id: T-0202
title: The demo backend and the server disagree about what a search matches
status: backlog
status: done
type: bug
priority: low
area: search
Expand All @@ -10,7 +10,12 @@ effort: S
scope: [packages/workfile/ui/src/api.demo.ts]
origin: [T-0195]
created: 2026-08-05
updated: 2026-08-05
updated: 2026-08-07
verified:
at: "2026-08-07T20:30:23.731Z"
method: local
commit: 61512e4dce848b0646b87f3c438a55f996a9b1d5
digest: "sha256:1ed51fc8633c7b6a2e868613e941fb56060417059078778996abdf6633c18390"
---

`searchScore` in `src/modules/records/index.ts` indexes a record body as whole
Expand All @@ -30,6 +35,20 @@ corpus, and that is a measured decision recorded in `query.ts`.

## Acceptance criteria

- [ ] The demo adapter and the server agree on what a query matches, field by field.
- [ ] The placeholder T-0195 wrote is exactly true against both.
- [ ] A test compares the two implementations over the same fixture rather than asserting each separately.
- [x] The demo adapter and the server agree on what a query matches, field by field.
- [x] The placeholder T-0195 wrote is exactly true against both.
- [x] A test compares the two implementations over the same fixture rather than asserting each separately.

## Activity

- 2026-08-07 20:20Z illodev@local#42eb42f5 · claimed
- 2026-08-07 20:30Z illodev@local#42eb42f5 · released

## Notes

- 2026-08-07 20:30Z illodev@local#42eb42f5 — The card scoped this to the three record list endpoints, and there was a fourth site with a rule of its own: the palette's lexical branch in api.demo.ts scored id and title only, weighted 100/50/25/10. The server answers a lexical search with the same ranker as its list routes — searchProjectRecordsHybrid falls through to searchProjectRecords when no provider is configured — so the palette in the hosted demo could not find a record by a word in its body at all, and ranked what it did find in an order the server never produces. All four sites go through the mirror now.

The mirror also carries the filter grammar and negation, not just the tokenizer. Leaving those out would have kept a disagreement the card does not mention: status:draft and -term were being matched as literal substrings.

One expectation of mine was wrong on the way and is worth leaving here. I first checked the whole-token rule with the query 'window' against 'windows', expecting the partial to find nothing. It finds four records, on both backends — a title carries Windows, and the title fallback matches a substring by design. The discriminator has to be a token no title carries and whose prefix no title token contains; 'surviving' / 'survivi' is one, picked from the snapshot rather than guessed.
- 2026-08-07 20:30Z illodev@local#42eb42f5 — local verification: Compared the two implementations directly over the same corpus and over a fixture. Against this repo's live server on 55 memory records, 12 queries agree on both the count and the ordered id list — whole words, partials, metadata-only hits, status: and tag: filters, a negation, and an accented pair. The parity test drives searchProjectRecords and the mirror over one fixture across 23 queries and is mutation-proven three ways: restoring the substring body match reports the card's exact symptom (nvoic finds DOC-0001 in the demo, not on the server), requiring terms as a phrase fails, and dropping the accent folding fails. In the built static demo in Chromium: surviving finds the one body that carries it and survivi finds nothing, and the palette now finds a body-only word at all, which the old id-and-title rule could never do. Full gate green: 471 + 10 tests.
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
---
id: T-0215
title: --help names 139 of the 232 flags the CLI accepts
status: review
status: done
type: bug
priority: high
area: core
created: 2026-08-07
updated: 2026-08-07
scope: [packages/workfile/bin/workfile.ts]
verified:
at: "2026-08-07T20:13:47.301Z"
method: local
commit: 61512e4dce848b0646b87f3c438a55f996a9b1d5
digest: "sha256:73450a43fa74404f8f7a25bc61c0ddcdd6b331841273571fa72f7c29a4b3e7d9"
---

`--help` documents 139 of the 232 flags the dispatcher accepts. The usage lines
Expand Down Expand Up @@ -42,7 +48,10 @@ asked whether what is real is taught.

Reported from Fube on 2026-08-07, where it had been costing one permission
dialog per protocol record.
- 2026-08-07 20:13Z illodev@local#42eb42f5 — local verification: Verified against the shipped dist binary, not the source: 63 subcommands under 17 command words, 232 accepted (word, flag) pairs — the card's own number — and every one of them is printed by its word's --help, word-bounded so --to is not satisfied by --tags. The three record creators each carry the curated --json-input usage line that only card create had. Guard mutation-proven: making the generated block drop one flag per row fails documentation.test.ts naming the exact flags, and the generator is read from COMMAND_FLAGS so it cannot drift from what the dispatcher accepts. Full gate green: 470 + 10 tests, strict ratchet with no new errors.

## Activity

- 2026-08-07 10:04Z illodev@local#bada1057 · backlog → review
- 2026-08-07 20:09Z illodev@local#42eb42f5 · claimed
- 2026-08-07 20:13Z illodev@local#42eb42f5 · released
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
---
id: T-0216
title: Patching a body drops the blank line under the frontmatter
status: review
status: done
type: bug
priority: medium
area: core
created: 2026-08-07
updated: 2026-08-07
scope: [packages/workfile/src/core/frontmatter.ts]
verified:
at: "2026-08-07T20:16:05.596Z"
method: local
commit: 61512e4dce848b0646b87f3c438a55f996a9b1d5
digest: "sha256:bc3c28399e93afe2c8d97190439f85c26917851205a7ec7c52482001f58b589b"
---

`createChangeFragment`, `createManagedDocument` and `createMemoryRecord` render
Expand Down Expand Up @@ -38,7 +44,10 @@ mixed line endings on exactly the line they were adding.

Reported from Fube on 2026-08-07, found by round-tripping a fragment through
`changelog patch` and reading the resulting `git diff`.
- 2026-08-07 20:16Z illodev@local#42eb42f5 — local verification: Verified end to end against the shipped binary in a scratch workspace. changelog patch on a committed fragment now produces a one-line git diff — the body — with the blank line under the frontmatter untouched, which is the card's own reproduction. A record whose blank line was spliced away by hand gets it back on the next patch, and the same holds for doc patch and memory patch, so the fix really is in the one shared writer rather than in one of the four call sites. A record converted to CRLF stays 11 CRLF and 0 bare LF across a patch, with the blank line itself CRLF. Writing the same body twice leaves both the content and the mtime untouched, so it is a genuine no-op and not an identical rewrite. Full gate green: 470 + 10 tests.

## Activity

- 2026-08-07 10:04Z illodev@local#bada1057 · backlog → review
- 2026-08-07 20:13Z illodev@local#42eb42f5 · claimed
- 2026-08-07 20:16Z illodev@local#42eb42f5 · released
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
---
id: T-0217
title: A documentation sites links are routes, and every one reads as broken
status: review
status: done
type: bug
priority: high
area: docs
created: 2026-08-07
updated: 2026-08-07
verified:
at: "2026-08-07T20:20:10.557Z"
method: local
commit: 61512e4dce848b0646b87f3c438a55f996a9b1d5
digest: "sha256:2219ee32df506a95737f379455aaac5c8c7dd27f9a0fafe6d5a53d13c57dd644"
---

A documentation site resolves `[text](guides/invoicing)` through its own router:
Expand Down Expand Up @@ -65,7 +70,9 @@ the scanner never saw it; erasing the backticks revealed a link whose target the
pattern then truncated at the first `)`, and two managed documents in Fube
turned from clean to **error**. Matching first and discarding what falls inside
code cannot invent a match that was not already there.
- 2026-08-07 20:20Z illodev@local#42eb42f5 — local verification: Verified against Fube's real help tree and against a fixture for the shapes a corpus cannot isolate. Fube live (1784 cards): 0 errors and 0 broken-link findings; the 8 dead links the criterion recorded have since been fixed there. Non-vacuity proven both ways on a copy of that 180-file tree in a scratch workspace: with routeRoots declared, 4 link findings (3 pointing at files outside the copy, 1 planted); with the same tree and the declaration removed, 683. So the scan runs and the route reading is what resolves them. Fixture: a bare link resolves onto .md, .mdx, index.md and index.mdx, and site-absolute /... too, with all five candidates listed in the finding's tried detail and only the deliberately dead one reported. Outside a route root a link is still a path — README's path link resolves and its dead one is found, tried listing the single file-relative candidate. Two links inside an unclosed fence are not followed. codeMask is exported from validation.ts and imported by documentation.test.ts, so the code reading is shared rather than written twice. Full gate green: 470 + 10 tests, strict ratchet clean.

## Activity

- 2026-08-07 10:25Z illodev@local#bada1057 · backlog → review
- 2026-08-07 20:20Z illodev@local#42eb42f5 · released
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
id: CHG-0153
title: The hosted demo matches a search the way a real workspace does
type: fixed
area: search
visibility: public
cards: [T-0202]
created: 2026-08-07
updated: 2026-08-07
---

The demo backend answered a query with a substring scan over the raw title, body, path and id, and its command palette had a third rule again — id and title only. The server tokenizes: a body matches by whole word, only a title falls back to a substring, and metadata and identity count too. So a partial word found a body in the demo and nothing against a real workspace, and the palette could not find a record by a word in its body at all. Both now run the same rule, filters and negation included, and a parity test drives the two implementations over one fixture.
Loading