TAGS: port the full calternal.js tag grammar (#task/, @context, +project, ##literal) and document it #403

Closed
opened 2026-09-29 05:24:26 +00:00 by kayg · 7 comments
Owner

Request (owner, 2026-09-29)

"that convention needs to be in our docs for me to remember even. and calternal should port all those conventions from js. and #x in tasks would be #task/x"

calternal.js tag grammar (source: calternal.js docs/search.md, docs/superpowers/specs/2026-06-26-tasks-system-design.md, packages/core/src/nlp/classifier.rs, dayfile.rs)

  • #x → #area/x in log entries and notes.
  • In tasks, #x → #task/x.
  • #area/… and #task/… are kept as written.
  • @x → context/x; +x → project/x.
  • ##x → the literal tag x (escape).
  • Tags are namespaced, space-joined, # kept; search matches tag:fitness against namespaced tags as calternal.js does.

Build

  1. Document the grammar in CONTEXT.md (vocabulary) and a user-facing docs page (docs/tags.md), ASD-STE100, with examples — the owner wants to be able to look it up.
  2. Port every rule to calternal-notes-core (composer parser, task parser, frontmatter tags, search tag matching) with the calternal.js test vectors as cross-language tests. calternal already maps #x → #area/x for entries; add the task, context, project and literal rules.
  3. Display rule for chips (from #359 T4): full namespaced tag where there is room, short leaf on dense blocks with the full tag on hover.
  4. No migration rewrites the User's existing files; existing tags keep their written form.
## Request (owner, 2026-09-29) "that convention needs to be in our docs for me to remember even. and calternal should port all those conventions from js. and #x in tasks would be #task/x" ## calternal.js tag grammar (source: calternal.js docs/search.md, docs/superpowers/specs/2026-06-26-tasks-system-design.md, packages/core/src/nlp/classifier.rs, dayfile.rs) - `#x` → `#area/x` in log entries and notes. - **In tasks, `#x` → `#task/x`.** - `#area/…` and `#task/…` are kept as written. - `@x` → `context/x`; `+x` → `project/x`. - `##x` → the literal tag `x` (escape). - Tags are namespaced, space-joined, `#` kept; search matches `tag:fitness` against namespaced tags as calternal.js does. ## Build 1. Document the grammar in CONTEXT.md (vocabulary) and a user-facing docs page (docs/tags.md), ASD-STE100, with examples — the owner wants to be able to look it up. 2. Port every rule to calternal-notes-core (composer parser, task parser, frontmatter tags, search tag matching) with the calternal.js test vectors as cross-language tests. calternal already maps `#x` → `#area/x` for entries; add the task, context, project and literal rules. 3. Display rule for chips (from #359 T4): full namespaced tag where there is room, short leaf on dense blocks with the full tag on hover. 4. No migration rewrites the User's existing files; existing tags keep their written form.
Author
Owner

Starting #403 on branch job/tags, based on d0d83a32b41e67b64f6586690ba4946e41878160 (dev). I’m reading the binding design and calternal.js sources, then I’ll commit the vocabulary and user guide before the parser and chip changes.

Starting #403 on branch `job/tags`, based on `d0d83a32b41e67b64f6586690ba4946e41878160` (`dev`). I’m reading the binding design and calternal.js sources, then I’ll commit the vocabulary and user guide before the parser and chip changes.
Author
Owner

Finding and decision: calternal.js packages/core/src/dayfile.rs::normalize_tag and composer.rs T4 tests pin Note composer #x as literal x, while Event mode maps it to area/x and Task mode maps it to task/x. The issue shorthand says #x maps to area/x in Notes. I will preserve the source's explicit Note composer behavior for cross-language parity, document the mode split, and leave existing User files unchanged.

Finding and decision: calternal.js `packages/core/src/dayfile.rs::normalize_tag` and `composer.rs` T4 tests pin Note composer `#x` as literal `x`, while Event mode maps it to `area/x` and Task mode maps it to `task/x`. The issue shorthand says `#x` maps to `area/x` in Notes. I will preserve the source's explicit Note composer behavior for cross-language parity, document the mode split, and leave existing User files unchanged.
Author
Owner

Finding: I added tag_operator_matches_leaf_parts_of_namespaced_tags with a Note tagged area/fitness and a Task tagged task/fitness. Before the fix, cargo test -p calternal-search tag_operator_matches_leaf_parts_of_namespaced_tags -- --nocapture failed because tag:fitness returned [] instead of both item IDs. Search used exact matching against only the full tag value.

Finding: I added `tag_operator_matches_leaf_parts_of_namespaced_tags` with a Note tagged `area/fitness` and a Task tagged `task/fitness`. Before the fix, `cargo test -p calternal-search tag_operator_matches_leaf_parts_of_namespaced_tags -- --nocapture` failed because `tag:fitness` returned `[]` instead of both item IDs. Search used exact matching against only the full tag value.
Author
Owner

Finding: dense Calendar chips previously removed only the area/ prefix, so other namespaces remained visible and the complete Tag was absent from hover and screen-reader text. I added a shared final-path-segment label for Agenda rows and timed Day blocks. The full Tag remains in hover text and accessible names. The targeted component suites pass: 12 tests passed. I merged the current local dev once into job/tags; production screenshot review is in progress.

Finding: dense Calendar chips previously removed only the `area/` prefix, so other namespaces remained visible and the complete Tag was absent from hover and screen-reader text. I added a shared final-path-segment label for Agenda rows and timed Day blocks. The full Tag remains in hover text and accessible names. The targeted component suites pass: 12 tests passed. I merged the current local `dev` once into `job/tags`; production screenshot review is in progress.
Author
Owner

Adversarial finding: one time-boxed pass sent 38 requests to the real local Search API: malformed operators and quotes, Unicode and NUL tag values, doubled slashes, an 8 KiB query, invalid limits, 24 concurrent tag searches, and a POST to the GET route. Results: no 5xx, timeout, connection failure, or crash; the malformed empty/operator/quote/NUL/oversized/limit cases returned 400, and POST returned 405. (((, tag:area//fitness, and Unicode tag queries returned 200 as ordinary search text and did not cause errors. I left those odd-but-harmless literal query cases unchanged.

Adversarial finding: one time-boxed pass sent 38 requests to the real local Search API: malformed operators and quotes, Unicode and NUL tag values, doubled slashes, an 8 KiB query, invalid limits, 24 concurrent tag searches, and a POST to the GET route. Results: no 5xx, timeout, connection failure, or crash; the malformed empty/operator/quote/NUL/oversized/limit cases returned 400, and POST returned 405. `(((`, `tag:area//fitness`, and Unicode tag queries returned 200 as ordinary search text and did not cause errors. I left those odd-but-harmless literal query cases unchanged.
Author
Owner

Final report

Implemented #403 on job/tags. The job branch is pushed. No User files were rewritten.

Changes

  • Documented tag vocabulary and behavior in CONTEXT.md and docs/tags.md.
  • Added tag_name_parts() in calternal-notes-core; the search index now matches tag:<name> against exact and namespaced tag parts.
  • Added shared cross-language tag vectors sourced from calternal.js rules and Rust parity tests.
  • Shortened dense Agenda and Day grid chips to the leaf label while retaining the full tag in hover and accessible text.

Changed files: CONTEXT.md, docs/tags.md, contracts/vectors/{README.md,tags.v1.json}, crates/calternal-notes-core/src/{lib.rs,nlp/mod.rs,nlp/tags.rs}, crates/calternal-notes-core/tests/vector_parity.rs, crates/calternal-search/src/indexer.rs, crates/calternal-search/tests/indexer.rs, packages/ui/src/tags.ts, packages/ui/src/components/calendar/{AgendaList.svelte,GridColumn.svelte}, apps/web/src/lib/calendar/{agenda.svelte.test.ts,TimeGrid.svelte.test.ts}.

Decisions

  • calternal.js defines bare #x as area/x in Event mode and task/x in Task mode, while Note mode keeps it as literal x. The docs and parity vectors preserve those source rules.
  • A trailing slash has no leaf segment, so the chip helper falls back to the full tag. This keeps malformed stored values visible and accessible.

Verification

  • cargo fmt --check: exit 0; no stdout or stderr.
  • cargo clippy --all-targets -- -D warnings: exit 0. Final output: Finished \dev` profile [unoptimized + debuginfo] target(s) in 141m 46s`
  • Full cargo test: stopped at the four-hour job time box while compiling dependencies; Ctrl-C exit 130. Last output: Compiling ed25519-dalek v2.2.0.
  • bun run check and full bun run test: not run due to the same time box.
  • Targeted Rust vector tests passed for calternal-notes-core and calternal-search; targeted UI tests passed: 2 files, 12 tests.
  • Production build succeeded (✓ built in 19.69s).
  • Adversarial Search API round: 38 requests, including malformed syntax, Unicode/NUL, oversized queries, invalid limits, concurrency, and a real POST. No 5xx, crash, timeout, disconnect, or hostile input acceptance. Empty/operator/quote/NUL/oversized/invalid-limit cases returned 400; POST returned 405. (((, tag:area//fitness, and Unicode tag queries returned 200 as ordinary search text; left unchanged as harmless search text.

Gate output

$ cargo fmt --check
<no output; exit 0>

$ cargo clippy --all-targets -- -D warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 141m 46s

$ cargo test
Compiling ed25519-dalek v2.2.0
<interrupted at the four-hour time box; exit 130>

$ bun run check
not run

$ bun run test
not run

Visual evidence

Production screenshots cover Agenda and Day grid at 390, 820, and 1440 px, in light and dark modes:

Commits and cleanup

Head: 61fd59f054b466628b811c4d3327d61273d09d4a (Merge branch 'dev' into job/tags). Changes were committed atomically in 19ecafdb, 82b3d410, f44e597b, and 3fea9b77; local dev was merged once. git push origin job/tags completed (Everything up-to-date). cargo clean completed; web build output was removed.

Known gap: full workspace cargo test and the two web gates remain unverified because the job reached the four-hour time box.

## Final report Implemented #403 on `job/tags`. The job branch is pushed. No User files were rewritten. ### Changes - Documented tag vocabulary and behavior in `CONTEXT.md` and `docs/tags.md`. - Added `tag_name_parts()` in `calternal-notes-core`; the search index now matches `tag:<name>` against exact and namespaced tag parts. - Added shared cross-language tag vectors sourced from calternal.js rules and Rust parity tests. - Shortened dense Agenda and Day grid chips to the leaf label while retaining the full tag in hover and accessible text. Changed files: `CONTEXT.md`, `docs/tags.md`, `contracts/vectors/{README.md,tags.v1.json}`, `crates/calternal-notes-core/src/{lib.rs,nlp/mod.rs,nlp/tags.rs}`, `crates/calternal-notes-core/tests/vector_parity.rs`, `crates/calternal-search/src/indexer.rs`, `crates/calternal-search/tests/indexer.rs`, `packages/ui/src/tags.ts`, `packages/ui/src/components/calendar/{AgendaList.svelte,GridColumn.svelte}`, `apps/web/src/lib/calendar/{agenda.svelte.test.ts,TimeGrid.svelte.test.ts}`. ### Decisions - calternal.js defines bare `#x` as `area/x` in Event mode and `task/x` in Task mode, while Note mode keeps it as literal `x`. The docs and parity vectors preserve those source rules. - A trailing slash has no leaf segment, so the chip helper falls back to the full tag. This keeps malformed stored values visible and accessible. ### Verification - `cargo fmt --check`: exit 0; no stdout or stderr. - `cargo clippy --all-targets -- -D warnings`: exit 0. Final output: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 141m 46s` - Full `cargo test`: stopped at the four-hour job time box while compiling dependencies; Ctrl-C exit 130. Last output: `Compiling ed25519-dalek v2.2.0`. - `bun run check` and full `bun run test`: not run due to the same time box. - Targeted Rust vector tests passed for `calternal-notes-core` and `calternal-search`; targeted UI tests passed: 2 files, 12 tests. - Production build succeeded (`✓ built in 19.69s`). - Adversarial Search API round: 38 requests, including malformed syntax, Unicode/NUL, oversized queries, invalid limits, concurrency, and a real POST. No 5xx, crash, timeout, disconnect, or hostile input acceptance. Empty/operator/quote/NUL/oversized/invalid-limit cases returned 400; POST returned 405. `(((`, `tag:area//fitness`, and Unicode tag queries returned 200 as ordinary search text; left unchanged as harmless search text. ### Gate output ```text $ cargo fmt --check <no output; exit 0> $ cargo clippy --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 141m 46s $ cargo test Compiling ed25519-dalek v2.2.0 <interrupted at the four-hour time box; exit 130> $ bun run check not run $ bun run test not run ``` ### Visual evidence Production screenshots cover Agenda and Day grid at 390, 820, and 1440 px, in light and dark modes: - Agenda: [390 light](https://git.kayg.org/attachments/61090708-5a03-47e8-898c-7661adf0a00a), [390 dark](https://git.kayg.org/attachments/7d2d3fb5-65cf-4169-aa24-b193a327f974), [820 light](https://git.kayg.org/attachments/7d62f5e5-6a2a-4c3e-a444-21637686400b), [820 dark](https://git.kayg.org/attachments/87d2abab-2792-4f06-a08f-cc8212e1fd46), [1440 light](https://git.kayg.org/attachments/05e6bfc3-6058-4185-aecb-ad7c305225ee), [1440 dark](https://git.kayg.org/attachments/01fbb7d8-e389-4b83-98ba-2f8579432ed9). - Day grid: [390 light](https://git.kayg.org/attachments/67d6a8c2-65ab-4663-ab4a-6fa2026d08fc), [390 dark](https://git.kayg.org/attachments/822db67c-2118-4222-a48c-5255e73f6e58), [820 light](https://git.kayg.org/attachments/3438c046-c483-4030-8b13-c453a3e94c63), [820 dark](https://git.kayg.org/attachments/bc85d479-d8a0-4d91-9089-d042ae2b95c7), [1440 light](https://git.kayg.org/attachments/c18ebfd4-c7f6-4440-a85b-e0e38af49d20), [1440 dark](https://git.kayg.org/attachments/63f998af-0c3d-433b-9203-ff0ce3c0e43c). ### Commits and cleanup Head: `61fd59f054b466628b811c4d3327d61273d09d4a` (`Merge branch 'dev' into job/tags`). Changes were committed atomically in `19ecafdb`, `82b3d410`, `f44e597b`, and `3fea9b77`; local `dev` was merged once. `git push origin job/tags` completed (`Everything up-to-date`). `cargo clean` completed; web build output was removed. Known gap: full workspace `cargo test` and the two web gates remain unverified because the job reached the four-hour time box.
Author
Owner

Merged into dev and pushed (43c1377c); orchestrator visual review passed. Deploy to calternal.cloud follows once the #356/#393 Apple GO/NO-GO clears.

Merged into dev and pushed (43c1377c); orchestrator visual review passed. Deploy to calternal.cloud follows once the #356/#393 Apple GO/NO-GO clears.
kayg closed this issue 2026-09-29 14:58:08 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#403
No description provided.