Commit graph

165 commits

Author SHA1 Message Date
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
Forgejo Actions
b75d7a8d7a Update changelog for v1.1.1 [skip ci] 2026-06-04 15:36:38 -07:00
fac39386d0 fix: self-update poll uses ureq (reqwest has no TLS backend)
All checks were successful
Build / validate (push) Successful in 4m31s
hephd's reqwest client is built default-features=false with no TLS
feature, so the self-update release poll's HTTPS GET always failed
('release check failed: requesting forge releases/latest') — the bug
never surfaced before because nothing in production used reqwest over
HTTPS (hub sync is plain http://). Switch the poll to ureq, which is
already a dependency and ships a rustls/ring TLS stack needing no system
libs (notably no cmake/aws-lc-sys, which would break the rust:bookworm CI
image). Verified end-to-end: a 0.0.0 build now detects v1.1.0, installs,
and restarts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:26:13 -07:00
Forgejo Actions
854d25c68b Update changelog for v1.1.0 [skip ci] 2026-06-04 15:07:55 -07:00
529f8b67d1 Merge pull request 'C2: hephd self-update (Mikado plan — cards for review)' (#7) from mikado/hephd-self-update into main
All checks were successful
Build / validate (push) Successful in 9m42s
Reviewed-on: #7
2026-06-04 15:03:22 -07:00
443763489b C2(hephd-self-update): finalize — single self-update how-to + changelog
All checks were successful
Build / validate (pull_request) Successful in 6m10s
Collapse the eight Mikado scaffolding cards (+ goal card) into one
user-facing how-to, docs/how-to/self-update.md: what self-update is and
how to enable it. The per-card breakdown was build-time scaffolding, not
documentation. Keeps the changelog fragment; updates the how-to index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:00:37 -07:00
240c8a9f68 C2(hephd-self-update): close service-env-forge-access
Public repo => anonymous HTTPS clone, no credentials (the SSH/canonical
premise was wrong: that was the access-restricted cargo registry, not git
clone). Install URL points at the canonical public host (verified end to
end); the service template bakes cargo onto PATH. Card rewritten to
reflect what actually happened.
2026-06-04 14:47:05 -07:00
59822d7257 C2(hephd-self-update): impl service-env-forge-access (public HTTPS, cargo on PATH)
The repo is public, so self-update needs no credentials: cargo install
--git is a plain anonymous clone (NOT the access-restricted Forgejo cargo
registry, which is what required forge.ops.eblu.me). Point INSTALL_GIT_URL
and the releases poll at the canonical public host over HTTPS — verified
end-to-end (cargo install --git https://forge.eblu.me/... --tag v1.0.3
builds a working hephd with zero auth).

Make the headless service able to run the apply path: 'heph daemon
start --self-update' (default off) generates a launchd/systemd service
that passes --self-update and bakes a PATH (incl ~/.cargo/bin) + HOME so
the minimal service env can find cargo. restart preserves the setting.
Default (no flag) services are byte-identical to before. Template + URL
behavior covered by unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 14:46:34 -07:00
20418240f7 C2(hephd-self-update): impl correct spawn_self_update_loop doc
All checks were successful
Build / validate (pull_request) Successful in 6m1s
The poller now installs + restarts (not just logs); fix the stale doc and
point at service-env-forge-access as the deployment step that makes the
apply path operational.
2026-06-04 13:56:27 -07:00
c237be6604 C2(hephd-self-update): close self-restart-after-update
Restarter + ProcessRestarter wired: install then exit(0) so the service
manager respawns the new binary; restart only on a successful install.
Unit-tested via injection.
2026-06-04 13:54:50 -07:00
bdcf4171a4 C2(hephd-self-update): impl self-restart-after-update (injectable Restarter)
Add a Restarter trait + ProcessRestarter (exit 0 so launchd KeepAlive /
systemd Restart=always respawn the new binary). apply_update now installs
then restarts, and the restart fires only on a successful install. Wired
into the poll loop. Unit-tested with fake installer+restarter: restart on
success, no restart after a failed install. Real process exit is never
run in tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:54:23 -07:00
e7cb86efdf C2(hephd-self-update): close cargo-install-from-tag
Installer trait + CargoInstaller + apply_update landed and unit-tested
via injection. Real cargo execution is gated on the deployment env
(service-env-forge-access).
2026-06-04 13:52:45 -07:00
3fab637353 C2(hephd-self-update): impl cargo-install-from-tag (injectable Installer)
Add an Installer trait + CargoInstaller (runs cargo install --locked
--git <ssh> --tag <tag> for heph/hephd/heph-tui/heph-quickadd — the
documented install command, via the SSH host that sidesteps the
cargo/forge canonical-name mismatch), and apply_update() which runs the
blocking install on the blocking pool. The poll loop now applies on a
detected update. Apply path is unit-tested with a fake installer (call +
failure paths); the real cargo subprocess is never run in tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:52:23 -07:00
fd76aa0b3a C2(hephd-self-update): close verify-hub-dropout-resilience
All checks were successful
Build / validate (pull_request) Successful in 5m24s
Spoke survival across hub downtime is now covered by a test; added a
client timeout so a black-hole hub can't stall the loop.
2026-06-04 13:47:53 -07:00
9213a943f5 C2(hephd-self-update): impl verify hub-dropout resilience (+ client timeout)
Lock in the base-case guarantee that a self-updating hub (which restarts
under its spokes) relies on. New sync_http test: a spoke whose hub is
unreachable keeps serving + accepting writes, a sync attempt fails fast
(Err, not hang/panic), and when the hub returns the accumulated ops
reconcile with no special recovery.

The verification surfaced one non-graceful path — the daemon's shared
reqwest client had no timeout, so a black-hole hub (connects, never
replies) could stall the sync/self-update loop. Give it a 30s timeout so
'the hub can vanish at any moment' holds even mid-request.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:47:26 -07:00
2a7a3ec270 C2(hephd-self-update): close service-respawn-on-clean-exit
systemd unit now Restart=always; both managers respawn after a clean exit.
2026-06-04 13:44:56 -07:00
544c8bba0e C2(hephd-self-update): impl systemd Restart=always for clean-exit respawn
Self-restart works by exiting cleanly and letting the service manager
respawn the new binary. launchd already does this (KeepAlive=true), but
the systemd user unit was Restart=on-failure, which ignores a clean
exit (code 0). Switch to Restart=always + RestartSec=1, update the unit
test, and note in run-the-daemon that existing Linux installs must
`heph daemon restart` once to regenerate the unit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:44:36 -07:00
758854478b C2(hephd-self-update): close self-update-poll-loop
All checks were successful
Build / validate (pull_request) Successful in 3m56s
Notify-only poller landed: ticks on the interval, logs when a newer
release is available. The daemon now self-reports update availability.
2026-06-04 13:42:48 -07:00
9fb200fe24 C2(hephd-self-update): impl self-update poll loop (notify-only)
Add a ReleaseSource trait (real ForgeReleaseSource over HTTP; injectable
for tests), check_release() returning a CheckOutcome
(UpToDate/UpdateAvailable/Failed) that never errors so a flaky forge
can't stall the daemon, and run_poll_loop() that ticks on the configured
interval and logs when a newer release is available. spawn_self_update_loop
now spawns the real poller. Detection is unit-tested with a stubbed source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:42:21 -07:00
35569b0649 C2(hephd-self-update): close self-update-opt-in-flag
--self-update flag + config plumbing landed; opt-in observable via the
startup log line.
2026-06-04 13:40:47 -07:00
f6bcd50684 C2(hephd-self-update): impl --self-update opt-in flag + config plumbing
Add --self-update (default off) and --self-update-interval-secs to the
hephd CLI, a SelfUpdateConfig (Some => enabled), and thread it into the
Daemon (with_self_update) for every mode. spawn_self_update_loop()
currently just announces the mode at startup ('self-update enabled')
so the opt-in is observable; the poll/apply cycle is wired in later
leaves. Omitting the flag leaves behaviour unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:40:16 -07:00
4a0094f955 C2(hephd-self-update): close release-poll-version-check
Version-compare + forge release parsing landed and unit-tested.
2026-06-04 13:37:14 -07:00
fad8f2f4de C2(hephd-self-update): impl release poll + version-check helpers
Add crates/hephd/src/selfupdate.rs: a pure update_available() that
compares the running heph_core::VERSION (e.g. "1.0.3 (sha)") against a
release tag ("v1.0.4") via semver, ignoring the build suffix and v
prefix; plus parse_latest_tag() / fetch_latest_tag() for the forge
releases/latest feed. Decision logic and JSON parsing are unit-tested
against sample payloads; the network fetch is isolated. Adds the semver
workspace dep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:36:55 -07:00
e6524fddbb C2(hephd-self-update): plan add goal + prerequisite cards for hephd self-update
All checks were successful
Build / validate (pull_request) Successful in 5m23s
Kick off the C2 Mikado chain for an opt-in (default-off) hephd
self-update mode (forge-poll -> cargo install from tag -> self-restart).
Goal card plus eight prerequisite cards, indexed from how-to.md:

  release-poll-version-check, self-update-opt-in-flag (leaves)
    -> self-update-poll-loop                 (notify-only core)
  service-env-forge-access (leaf, the cargo/forge blocker)
    + self-update-poll-loop -> cargo-install-from-tag
  service-respawn-on-clean-exit (leaf, systemd Restart=always)
    + cargo-install-from-tag -> self-restart-after-update
  verify-hub-dropout-resilience (leaf, lock in the base-case guarantee)

Grounded in research of hephd's sync loop, daemon lifecycle, the
launchd/systemd service templates, and the forge releases API.
Captured from Hephaestus task 01KTA2NSNRYT902HC3VRW00S1J.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:20:46 -07:00
Forgejo Actions
254c83036b Update changelog for v1.0.3 [skip ci] 2026-06-04 12:42:46 -07:00
e4f1fd5ff8 Merge pull request 'feat: CLI quick wins — heph-tui --version, fuzzy --project, heph context, version RPC' (#6) from feature/cli-quick-wins into main
All checks were successful
Build / validate (push) Successful in 5m51s
Reviewed-on: #6
2026-06-04 12:42:19 -07:00
7f48a2a1c5 infra: add cargo-fmt-check pre-push prek hook (mirror CI)
All checks were successful
Build / validate (pull_request) Successful in 5m25s
The cargo-fmt failure on this PR slipped to CI because the pre-commit
prek hooks were never installed in the working clone. The existing
cargo-fmt hook reformats in place but only when it runs. Add a
pre-push cargo-fmt-check hook (`cargo fmt --all --check`) that mirrors
CI's Dagger `check` step exactly, so an unformatted commit is blocked
locally before it can reach the runner — even if the pre-commit hook was
skipped or not installed. Filtered to .rs pushes so Rust-free pushes pay
nothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 12:36:12 -07:00
8d80208726 style: rustfmt the new rpc_socket tests
Some checks failed
Build / validate (pull_request) Has been cancelled
The project.resolve socket test had a node.create call over rustfmt's
line limit; CI's `cargo fmt --all --check` flagged it. Wrap it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 12:31:01 -07:00
7914232ec4 feat: add a version RPC returning the daemon build version
Some checks failed
Build / validate (pull_request) Failing after 15s
RPC clients (the hephaestus.nvim plugin, for its `:Heph version`
command) had no way to learn which hephd they are talking to — `health`
returns counts, not a version. Add a tiny `version` method returning
`{ version: heph_core::VERSION }`, the same `X.Y.Z (sha)` string the
binaries print for --version. No store access needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 11:13:47 -07:00
babdb21c0a feat: heph context <task-id> reads/edits a task's context doc by id
Editing a task's canonical-context doc body previously meant looking up
its `canonical_context_id` (e.g. via `heph list --json`) and then
`heph node update <doc-id> --body`. Add a `heph context <task-id>`
command that resolves the canonical-context doc from the task's outgoing
links and:

  * prints the body with no flag,
  * `--body <text>` replaces it (`-` reads stdin, matching `node update`),
  * `--append <text>` adds a blank-line-separated paragraph.

Errors clearly when the id has no canonical-context doc (e.g. a plain
doc node rather than a task). Purely a client-side CLI convenience — no
new RPC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 11:09:53 -07:00
fc25f6ac51 feat: --project arg is case-insensitive / prefix-fuzzy when unambiguous
The `--project <name>` argument matched titles case-sensitively and
exactly, so `--project hephaestus` or `--project heph` failed against a
`Hephaestus` project. Make project-name resolution forgiving but
deterministic, via a tiered match in `resolve_project_id`:

  1. exact (case-sensitive) — the historical behavior; always wins
  2. case-insensitive exact — only when unambiguous
  3. case-insensitive prefix — only when unambiguous

Ambiguous fuzzy matches resolve to None (callers report "no project
named X") rather than silently picking one. This single resolver already
backed `heph list --project` (via project_scope); route the CLI's
task/edit/promote/parent path through it too with a new `project.resolve`
RPC + `Store::resolve_project`, so every `--project` surface behaves the
same.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 10:57:37 -07:00
58a5544d44 feat: heph-tui --version reports version + build SHA
heph-tui was the one daily-driver binary that did not answer --version.
Add the same clap `version = heph_core::VERSION` attribute that heph and
hephd already carry, so all three report `X.Y.Z (sha)` consistently.

Addresses the heph-tui half of the cross-binary --version task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 10:52:52 -07:00
Forgejo Actions
67627a0930 Update changelog for v1.0.2 [skip ci] 2026-06-04 10:22:04 -07:00
1603b37f74 fix: --version no longer false-positives -dirty on clean release installs
All checks were successful
Build / validate (push) Successful in 6m13s
The build-time dirty check used `git status --porcelain`, which counts
untracked files — including cargo's own `.cargo-ok` marker in a
`cargo install --git` checkout — so a clean tagged build reported e.g.
`1.0.1 (bcab3c16b-dirty)`. Use `git diff --quiet HEAD` (tracked changes only).

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