Commit graph

198 commits

Author SHA1 Message Date
b33cafe2e0 feat(core)!: reject manual creation of derived/internal link types
All checks were successful
Build / validate (pull_request) Successful in 9m34s
A hand-made `wiki` link row looked like it worked, then silently
vanished on the next body write — the links table's wiki rows are an
index derived from `[[…]]` in the body, and `sync_wiki_links` diffs
them against it. Store::add_link now rejects `wiki` (with a pointer at
the body as the durable path) plus the `canonical-context` / `log-of`
task internals. Body materialization uses the internal path and is
unaffected; `link.add` ops arriving over sync still apply.

Drops the wiki-links explanation card — it existed to document the
footgun, and the footgun is gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 11:24:36 -07:00
8417f70326 feat(tui): undoable delete, rename, project re-parent, conflicts view, full log view
All checks were successful
Build / validate (pull_request) Successful in 9m21s
- D (task) and D (project) are now undoable with u: task delete restores
  via node.restore; project delete restores the node and re-files the
  tasks the delete unfiled. Redo re-deletes.
- R renames the highlighted task in place (prefilled input modal),
  keeping its canonical-context doc's title in step; undoable.
- m on a sidebar project opens the move picker in re-parent mode —
  self + descendants excluded, "(Move to root)" detaches; undoable.
- C opens a conflicts review in the center pane: per-row node title,
  field, and both values; l/r keeps local/remote (applies the value via
  the new conflicts.resolve semantics) and refreshes the chip.
- L opens the highlighted task's full scrollable log (the preview pane
  shows only the last five lines).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 11:12:06 -07:00
3db026f6e5 feat: actionable conflicts, node restore, project move, task-aware show
- conflicts.resolve now applies the chosen value (local/remote) as a new
  task.set op — peers converge on the decision — instead of only marking
  the row resolved.
- heph node restore <id> — undo of node rm.
- heph project move <name> --parent <p>|--root — post-creation
  re-parenting from the CLI.
- heph show on a task prints its canonical-context doc body (the task
  node's own body is always null and was hiding the real content).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 11:03:13 -07:00
8fe11c75cd feat(hephd): --owner-id flag — one-step Path-A hub seeding
All checks were successful
Build / validate (pull_request) Successful in 13m12s
A fresh hub started with an existing device's owner id rebuilds itself
entirely from that spoke's first full op-log push: no snapshot copy and
no origin reset (the new store mints its own). adopt_owner is idempotent
once adopted, so the flag is safe baked into a service unit.
[[set-up-sync-hub]] documents the recipe and drops the manual-seeding gap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:55:17 -07:00
0e5bed3282 feat: daemon status surfaces runtime config + self-update state
sync.status now carries a runtime block — version, mode, sync cadence,
and self-update state (interval + last check/outcome, tracked by a new
SelfUpdateHealth shared with the poll loop). `heph daemon status` asks
the live daemon and prints it under the service facts: hub + oidc, sync
health at a glance, open-conflict count, self-update status.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:53:05 -07:00
e65e2d3910 feat(hephd): sync observability — recovery, per-cycle volume, throttled failures
Background sync now logs cycles that move ops at info (pulled/applied/
pushed + the cursors they advanced to), announces recovery with the
length of the failure streak it ends, and suppresses repeats of an
identical failure to one warn per ten cycles. SyncReport carries the
advanced cursors (additive, wire-compatible).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:49:37 -07:00
66d78ac39a fix(hephd): https hub urls work; sync errors name the phase and hub
reqwest was built with no TLS backend, so any https --hub-url failed
with a bare "error sending request". Compile in rustls (platform trust
store via rustls-platform-verifier) and wrap each sync phase's errors
with the url and phase so failures are actionable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:45:56 -07:00
9189543b4c feat(hephd): node.restore and project.reparent RPCs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:43:55 -07:00
aea7a51860 feat(core): export expands [[id]] wiki-links to readable labels
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:42:32 -07:00
32197bd170 feat(core): reparent_project — change a project's parent after creation
Tombstone the old parent link, add the new one (or none for root), with
non-project endpoints and cycle-creating moves rejected via the existing
project_subtree traversal. Pure link ops — no schema or sync change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:41:41 -07:00
f62836b1b4 feat(core): node.restore op + tombstone cascade to task attachments
Tombstone/restore are an LWW pair keyed by their own op HLCs, with the
winner derived from the op-log at apply time — no schema change, and the
old monotonic rule survives as the no-restores degenerate case.
Tombstoning a node now cascades to its canonical-context and log docs
(no more orphaned FTS leftovers); restore revives them symmetrically.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:40:20 -07:00
05212133ac docs: task-sweep end state — wiki-links explanation + changelog fragments
All checks were successful
Build / validate (pull_request) Successful in 5m33s
Docs-first commit for the feature/task-sweep branch, which addresses the
bulk of the outstanding Hephaestus-project tasks in one sweep. The
fragments describe the intended end state; code follows.

Also delivers the "understand heph link wiki semantics" task outright:
the new [[wiki-links]] explanation card documents body-vs-links-table
layering, the manual-wiki-link reconciliation footgun, resolution tiers,
and per-client surfaces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:33:59 -07:00
Forgejo Actions
19ababc57f Update changelog for v1.4.2 [skip ci] 2026-06-09 09:16:12 -07:00
2911f418a5 Merge pull request 'Attention as a1–a4: set bands directly, retire cycling' (#17) from feature/attention-a1-a4 into main
Some checks failed
Build / validate (push) Failing after 6m30s
Reviewed-on: #17
2026-06-09 09:15:32 -07:00
730863b832 feat(heph): accept a1-a4, 1-4, or colour words for -a/--attention
All checks were successful
Build / validate (pull_request) Successful in 8m24s
The CLI's attention flag (on task/list/attention/edit/promote) now takes the
a1–a4 labels, a bare digit 1–4, or a colour word, normalizing to the storage
colour before the RPC. Adds Attention::parse_input() in heph-core (lenient
human input) alongside the strict storage parse(), with a clear error listing
the accepted forms. `heph attention` now echoes the band as `a1 (red)`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 08:29:46 -07:00
ebb2366236 feat(attention): set bands directly as a1–a4 instead of cycling
Retire the `A` attention cycle and the duplicate `b` push-to-blue gesture
in heph-tui. Attention is now picked directly: press `a` then `1`–`4`
(a1=red, a2=orange, a3=white, a4=blue, ordered by intensity). Cycling past
blue used to make a task vanish from the current view with no way back —
direct selection never does. Quick-add moves from `a` to `n`.

Surface the a1–a4 nomenclature everywhere instead of colour words or the
old p1–p4 priorities: heph-tui status/legend, the heph-quickadd chip + hint,
and the PWA chip/hint plus a new band-picker (replacing its cycle button).
The shared quick-add parser now accepts `a1`–`a4` (a1=red … a4=blue) and no
longer recognizes `p1`–`p4`. Colour mappings are unchanged; only the words.

Add Attention::ui_label() in heph-core so both Rust surfaces share the
mapping; bump the PWA service-worker cache; update the PWA how-to.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 07:50:53 -07:00
Forgejo Actions
b34371af87 Update changelog for v1.4.1 [skip ci] 2026-06-08 20:24:38 -07:00
17dab0e281 Merge pull request 'fix(quickadd): return focus to the previous app when the ⌘' popover hides' (#16) from feature/quickadd-focus-return into main
All checks were successful
Build / validate (push) Successful in 9m38s
2026-06-08 20:22:05 -07:00
470ef1de0e fix(quickadd): return focus to the previous app when the popover hides
All checks were successful
Build / validate (pull_request) Successful in 5m52s
The global ⌘' quick-add overlay is a borderless, transparent, always-on-top
accessory window that winit hides with `Visible(false)`. That orders the window
out visually but leaves heph-quickadd the *active* application — so after a
capture (or Esc / toggle) keyboard focus never returns to the app the user was
in, and the lingering overlay can keep intercepting clicks where it used to sit.

Hide at the application level instead via `NSApplication.hide:`, which fully
orders our windows out and activates the next app in line (the previously
focused one). On re-show, `unhide:` clears that hidden flag before the existing
viewport `Focus` command makes the field key again. Both are macOS-only no-ops
elsewhere, wired through new `app_yield_focus`/`app_take_focus` helpers backed by
objc2 / objc2-app-kit (unified to the 0.6/0.3 line global-hotkey already pulls).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 20:08:07 -07:00
aec807fd28 Merge pull request 'Reconnect the socket client across daemon restarts (heph-tui survives self-update)' (#15) from feature/client-reconnect into main
All checks were successful
Build / validate (push) Successful in 13m7s
2026-06-08 15:22:05 -07:00
b04a71421e fix(hephd): reconnect the socket client across daemon restarts
All checks were successful
Build / validate (pull_request) Successful in 8m7s
`Client` connected to the unix socket once and never reconnected, so after an
opt-in self-update or `heph daemon restart` dropped the socket, every later
`call()` failed — `heph-tui` would sit on errors until relaunched (and the work
we just shipped makes restarts more frequent).

`Client` now stores the socket path and reconnects on a dropped connection,
classifying the failure to stay safe:
- write-side failure (request never reached the daemon) → reconnect + retry once;
- reply lost after sending (daemon closed mid-request) → reconnect for next time
  but surface this one, so a mutation is never silently double-applied;
- genuine RPC errors are passed through untouched.

heph-tui and the CLI use `Client` unchanged, so the TUI self-heals on its next
refresh tick. Adds an integration test driving a mock daemon that drops the
connection after each request.

Closes the "heph-tui: reconnect on a dropped daemon socket" backlog task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 15:19:10 -07:00
5c2b4bde2c Relabel changelog v1.3.0 section as v1.4.0 [skip ci]
A double workflow_dispatch produced both v1.3.0 and an empty duplicate v1.4.0
(the version actually deployed via self-update). Move the release notes onto
v1.4.0 to match what shipped; v1.3.0 release+tag are being removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:35:10 -07:00
Forgejo Actions
2ca1e246f0 Update changelog for v1.3.0 [skip ci] 2026-06-08 14:15:03 -07:00
9a4f18fbd5 Merge pull request 'Auth errors: distinguish IdP rejection from unreachable + actionable re-auth recovery' (#14) from feature/auth-error-clarity into main
All checks were successful
Build / validate (push) Successful in 11m58s
2026-06-08 14:10:35 -07:00
e943a940f1 feat(hephd,heph,heph-tui): distinguish IdP rejection from unreachable + actionable re-auth
All checks were successful
Build / validate (pull_request) Successful in 6m12s
The spoke OAuth path funneled every failure into one `AuthError::Provider`
whose Display was hardcoded "identity provider unreachable". So a reachable IdP
returning `400 invalid_grant` on a refresh was reported as "unreachable",
misdirecting incident response toward the network when the fix is re-auth. The
real refresh cause was also swallowed — `bearer()` logged it and returned None,
so sync health only ever showed the downstream 401 on /sync/pull.

Wording fix (auth.rs / oauth.rs):
- Split AuthError into Unreachable (transport), Rejected (IdP returned an HTTP
  error — carries the RFC 6749 §5.2 error/error_description), and Other
  (keyring / malformed response, previously mislabeled too).
- refresh()/discover()/start()/poll() classify transport vs status; refresh
  reads the OAuth error body on a non-2xx.
- Hub-side token verify maps IdP-infra failures → 503, token failures → 401.

Recovery UX (server.rs / heph / heph-tui):
- bearer() returns Result; the sync paths record the real acquisition failure
  (with a re-login hint when it's a rejection) instead of a masked 401.
- sync health's last_error carries the exact `heph auth login --hub-url …
  --issuer … --client-id …` command (keyed to the configured hub); sync.status
  also returns issuer/client_id + the command.
- New `heph auth status` prints auth health and the re-login command.
- heph-tui's auth chip points at it: `⚠ auth · heph auth status`.

Closes the duplicate "misleading identity provider unreachable" tasks and the
"actionable re-auth guidance" task. Also corrects a now-stale set-up-sync-hub
gap note (daemon config baking landed in the prior PR).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:06:08 -07:00
b82264892f Merge pull request 'Fix macOS heph daemon restart bootout→bootstrap race (5: Input/output error)' (#13) from feature/daemon-restart-race into main
All checks were successful
Build / validate (push) Successful in 11m52s
2026-06-08 13:43:55 -07:00
f6b27414a8 fix(heph): make macOS heph daemon restart race-free
All checks were successful
Build / validate (pull_request) Successful in 8m39s
`restart` bootstrapped immediately after `bootout`, but `launchctl bootout` is
asynchronous: launchd may still be killing/reaping the job and removing its
label when the command returns. Bootstrapping into that transitional domain
fails with a generic `5: Input/output error`, intermittently — the odds depend
on how fast hephd (sync client + SQLite + a heph-quickadd child) shuts down.

- Wait for the label to actually clear (poll `launchctl print`, bounded) before
  re-bootstrapping, and retry the bootstrap to cover the residual settle window.
- When the plist is unchanged (the common binary-upgrade restart), use
  `launchctl kickstart -k` to restart the loaded job atomically — no
  bootout/bootstrap, no race. The full reload path is reserved for genuine
  config changes, where launchd must re-read the plist.

Start's bootstrap shares the same retry helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 13:38:47 -07:00
5535cc7127 Merge pull request 'heph daemon: bake mode/hub/oidc/self-update-interval into the service' (#12) from feature/daemon-self-update-interval into main
All checks were successful
Build / validate (push) Successful in 7m44s
2026-06-08 13:32:46 -07:00
626c796e6c feat(heph): bake daemon mode/hub/oidc/self-update-interval into the service
All checks were successful
Build / validate (pull_request) Successful in 5m45s
`heph daemon start`/`restart` previously hardcoded `hephd --mode local` and
only wired the bare `--self-update` bool — the poll interval and all spoke/hub
sync config (`--hub-url`, `--http-addr`, `--oidc-*`) could not be set on the
managed service without hand-editing the plist/unit (which a later
start/restart would clobber).

Generate the hephd arg vector from a DaemonConfig and add the corresponding
`heph daemon start/restart` flags: --mode, --hub-url, --http-addr,
--oidc-issuer, --oidc-audience, --oidc-client-id, and
--self-update-interval-secs. Regenerating now reads the existing service file
and preserves any flags not passed (start as well as restart), so a bare
invocation never silently drops baked config.

Closes the "pass through --self-update-interval-secs" and "bake hub/spoke
config into the generated service" backlog tasks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 13:25:15 -07:00
c9bb2cbe64 feat(heph-tui): show sync age in seconds under a minute
All checks were successful
Build / validate (push) Successful in 6m28s
The background sync loop runs every 30s, so the last-sync age never crossed
the 60s 'just now' threshold — the chip always read 'just now', which also
masked the first missed sync (age 30-60s looked identical to a fresh one).
Show seconds under a minute ('⟳ 26s') so the chip is a visible heartbeat and a
stalled sync surfaces ~30s sooner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 11:24:09 -07:00
Forgejo Actions
1a8752f124 Update changelog for v1.2.3 [skip ci] 2026-06-06 11:03:45 -07:00
02a8dd5180 Merge pull request 'heph-tui sync health: last-sync age, pending conflicts, auth-failure indicator' (#11) from feature/tui-sync-health into main
All checks were successful
Build / validate (push) Successful in 8m0s
2026-06-06 11:03:00 -07:00
11aa25c9f4 feat(heph-tui,hephd): surface sync health (last-sync age, conflicts, auth failure)
All checks were successful
Build / validate (pull_request) Successful in 6m11s
A spoke could be silently failing to sync (expired token → 401, or hub
unreachable) with the only signal buried in the daemon log. Now:

- hephd tracks SyncHealth (last attempt/success time, last error, auth-failure
  flag) from the background sync loop and sync.now, classifying a 401 as an auth
  failure. sync.status returns it plus the pending merge-conflict count.
- heph-tui shows a live status-line indicator (spoke only): '⟳ <age>' since the
  last good sync, red '⚠ auth' when re-login is needed, '⚠ offline' when the hub
  is unreachable, and '⚠ N conflicts' when conflicts are pending. The event loop
  polls on a 2s tick so the age advances and failures appear while idle.
- docs: recommended Authentik access/refresh token validity to stop frequent
  re-logins (with the iOS PWA localStorage-eviction caveat).

Closes the 'Add hub connection status to heph-tui' and 'Spoke sync health:
surface unhealthy state instead of silent 401 spam' backlog items.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 10:19:11 -07:00
Forgejo Actions
4bf255b211 Update changelog for v1.2.2 [skip ci] 2026-06-06 09:30:28 -07:00
b2ddb41a46 Merge pull request 'heph-tui + PWA cosmetic polish: humanized recurrence, scrolling/indented/counted project sidebar' (#10) from feature/tui-polish-project-tree into main
All checks were successful
Build / validate (push) Successful in 4m38s
2026-06-06 09:29:10 -07:00
9a487cbe3b feat(heph-tui,heph-pwa): humanized recurrence + indented/counted/scrolling project sidebar
All checks were successful
Build / validate (pull_request) Successful in 6m57s
Bundles the cosmetic/UI-polish backlog for the agenda surfaces. All read-side;
no schema or sync change (see hub-spoke-data-evolution).

- humanize_rrule (hephd::datespec): inverse of parse_recurrence — renders an
  RRULE as 'every other week', 'weekdays', 'yearly on Apr 15', etc.; falls back
  to the raw rule for unmodeled parts (COUNT/UNTIL/ordinal BYDAY). Mirrored in
  the PWA's datespec.js. Shown in the TUI recurs detail line and PWA task/qa
  previews instead of the raw FREQ= string.
- project.overview RPC + Store::project_overview: each project's parent (via the
  existing 'parent' links) and direct outstanding-task count, a read-only query.
- TUI sidebar: subprojects indented by depth, per-project counts, wider pane,
  and ListState + scrollbar so it scrolls instead of clipping on overflow.

Tests: humanize parity (Rust + JS), round-trip through parse_recurrence,
raw-passthrough; project_overview count/parent; sidebar tree ordering + cycle
safety.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 17:44:43 -07:00
00da36c637 doc(explanation): hub+spoke data-evolution / migration rules
All checks were successful
Build / validate (pull_request) Successful in 6m18s
Document why heph's op-based sync lets most new features (new link types,
read-side queries, optional payload fields) ship without a coordinated
migration across the hub and spokes, and the narrow case — a new required
SQLite column the apply path writes — that does need a hub-first rollout.

Groundwork for the indented/counted project sidebar, which is pure read-side
(existing parent links + a GROUP BY) and needs no migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 17:31:11 -07:00
Forgejo Actions
c8512b2b50 Update changelog for v1.2.1 [skip ci] 2026-06-05 07:36:46 -07:00
36bd27226f Merge pull request 'heph-pwa: Login with Authentik (Authorization Code + PKCE)' (#9) from heph-pwa-oidc-login into main
All checks were successful
Build / validate (push) Successful in 7m46s
Reviewed-on: #9
2026-06-05 07:32:26 -07:00
1f81a2e6d9 feat(heph-pwa): Login with Authentik (Authorization Code + PKCE)
All checks were successful
Build / validate (pull_request) Successful in 6m31s
Replace the manual bearer-token paste with a proper browser OIDC sign-in.

- Hub: unauthenticated GET /config -> {issuer, client_id} (added after the auth
  layer), sourced from the verifier's new TokenVerifier::oidc_config(). Lets the
  PWA self-configure when served from the hub. Tests in web_serve.rs.
- PWA: src/oauth.js implements PKCE (S256), the authorize redirect, the callback
  token exchange, and silent refresh (offline_access). Settings gains a "Login
  with Authentik" button (manual token kept under a fallback disclosure); rpc.js
  retries once on 401 via a refresh hook; app.js completes the callback / refreshes
  on load; sw.js skips caching the callback URL and ships oauth.js in the shell.

Requires the PWA origin registered as a redirect URI on the Authentik provider.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 07:17:05 -07:00
a0be0f1085 doc(heph-pwa): in-app Authentik login replaces manual token paste
Document the PKCE 'Login with Authentik' flow, the hub /config zero-config
discovery, and the redirect-URI prerequisite on the Authentik heph provider.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 07:09:42 -07:00
Forgejo Actions
5f3e3225ec Update changelog for v1.2.0 [skip ci] 2026-06-04 17:51:55 -07:00
052f624e6f Merge pull request 'heph-pwa: mobile app (PWA mirror of heph-tui) + hub static serving' (#8) from feature/heph-pwa-mobile into main
All checks were successful
Build / validate (push) Successful in 6m44s
Reviewed-on: #8
2026-06-04 17:50:47 -07:00
936c2635ef doc(heph-pwa): production runbook — host the app from the hub (indri) with OIDC
All checks were successful
Build / validate (pull_request) Successful in 6m18s
Add host-heph-pwa.md: a deployment how-to for serving the PWA from the canonical
hub in the hub/spoke OIDC setup (post-release) — fetch the shell at the hub's
tag, add --web-root, terminate TLS (tailscale serve / reverse proxy), and the
token-paste caveat with the device-code-login follow-up. Cross-linked from
heph-pwa and the how-to index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:17:25 -07:00
271c609c14 feat(heph-pwa): re-fetch the current view when the app regains focus
The PWA shares the daemon's store with the TUI/desktop popover but only
re-fetched on a view switch or action — so a task marked done elsewhere left a
stale list on screen. Reload the current view on visibilitychange→visible
(switch back to the phone, unlock, tab re-show), skipping it mid-modal/search.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:16:15 -07:00
0036c1a284 fix(hephd): supervise the ⌘' popover in server mode too; PWA defaults hub to its origin
Popover supervision was gated to Mode::Local, so running the store-owning
daemon in server mode (now needed to host heph-pwa) silently dropped the
desktop quick-capture popover. Server mode is local + an HTTP hub and owns the
same store/socket, so it should drive the popover too; broaden the guard to
Local | Server (client, a thin proxy, still opts out).

Also: when the PWA shell is served from the hub, default the hub URL to its own
origin so the app is zero-config on first open (Settings still overrides). Bump
the service-worker cache to v2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:13:28 -07:00
b24a148add doc(heph-pwa): how-to card, index entry, changelog fragment
Document serving the app from the hub (--web-root), connecting (hub URL +
optional token), quick-add syntax, voice, triage, and the deliberate
design choices (PWA over native iOS; online-only; token paste vs device flow)
with their known limitations to revisit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 16:59:38 -07:00
4baa8e1c9d feat(heph-pwa): mobile app shell — views, quick-add, triage, search, voice
A buildless, installable PWA that mirrors heph-tui: sidebar of built-in views
(tom/tasks/work/chores/ondeck/inbox) + projects, a task list with attention
flags / project bullets / date chips, tap-to-expand triage (done/drop/skip/
attention/reschedule/move/delete + undo), full-text search, and a read-only
context+log preview. The primary surface is the quick-add modal (FAB or Cmd-'),
which live-parses the TUI syntax into preview chips and supports voice via
on-device dictation / the Web Speech API. rpc.js is the online-only JSON-RPC
client mirroring heph-tui's Backend; settings persist in localStorage. Service
worker caches the app shell for offline launch.

Verified end-to-end against a local server-mode hephd (--web-root): the app
boots, calls the view RPC, and renders RankedTasks in headless Chrome.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 16:59:37 -07:00
c3111d498b feat(heph-pwa): port quickadd + datespec parsers to JS (with parity tests)
Faithful JS ports of hephd's quickadd.rs / datespec.rs so the PWA's quick-add
accepts the identical syntax (p1-4, #Project greedy match, today/+3d/fri/ISO,
'every …' recurrence) and produces the same RRULEs and local-midnight do-dates
as the CLI/TUI. test/parsers.test.mjs replays the Rust unit cases under
`node --test` (13/13 pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 16:42:09 -07:00
ca8f7d1ab2 feat(hephd): CORS + optional static serving on the hub HTTP endpoint
Add a permissive CORS middleware (answers the browser OPTIONS preflight and
stamps Access-Control-* on every response) and an optional --web-root static
file handler with an index.html SPA fallback. Together these let a browser
surface — the forthcoming heph-pwa mobile app — call /rpc cross-origin or be
hosted same-origin straight from the hub. No new crate dependencies; file
reads run on the blocking pool. Covered by tests/web_serve.rs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 16:39:20 -07:00