227 Commits

Author SHA1 Message Date
teamchong c44cdf4258 chore: release 0.4.0 v0.4.0 2026-06-19 16:53:11 -04:00
teamchong 8b15525cb9 fix(transform): relocate cache anchor onto history image (stop 1.25x re-create)
The history image sits after the slab in prefix order, but the single cache
breakpoint was anchored on the slab. So the largest block — the ~141k-token
history image — only cached when the caller's roaming downstream marker happened
to land after it. When it didn't, the whole history image re-created at the 1.25x
cache_create rate every turn instead of amortizing into 0.1x reads. On a warm
session that turned a real compression win into a net loss (the "411% more"
the dashboard correctly surfaced).

Fix is pure relocation: move pxpipe's single breakpoint from the slab onto the
LAST history image (which sits after the slab), so slab + history cache as one
stable prefix — created once, then read. Marker count is unchanged: pxpipe still
never ADDS a marker (Task #21), it only moves the caller's existing one. When no
slab imaged, the history path keeps its own relocation.

- transform.ts: relocateAnchorToHistoryImage on the collapse-success branch
- history.test.ts: two old "history carries no marker" invariants upgraded to
  assert the relocation (last history image carries it; exactly one marker total)
2026-06-19 16:39:46 -04:00
teamchong b20c976f61 fix(dashboard): restore Saved + Details on replayed rows after restart
replay() rebuilt RecentRow from the JSONL but never set
session_saved_so_far_delta, img_id, or contextHistory — so after a restart
old rows showed "-" in Saved and had no Details link, while live rows did.
Live update() does all three; replay() now matches.

- replay() sets session_saved_so_far_delta (baselineInputEff - actualInputEff,
  same cache-weighted math as live) so the Saved column survives a restart.
- replay() rebuilds the Context Map breakdown from persisted fields
  (baseline_tokens, bucket_chars, image_count, usage) with a synthetic
  nextImageId, and sets img_id/img_ids so the Details link resolves.
- The PNG ring is in-memory and not restorable; restored entries carry
  imageIds:[] and a `restored` flag, and the Details panel shows
  "thumbnails expired when the proxy restarted" instead of dead <img>s.
- +2 replay tests (restart-restore.test.ts).

376 tests pass; typecheck + build clean.

Claude-Session: https://claude.ai/code/session_01RV8uiMYNhrAdTUzJ9fkpcp
2026-06-19 16:06:59 -04:00
teamchong ad526fce31 fix(ci): regenerate pnpm-lock.yaml after canvas moved to devDependencies
@napi-rs/canvas was moved dependencies -> devDependencies (edge-safe
packaging) but the lockfile still listed it under dependencies, so CI's
`pnpm install --frozen-lockfile` refused the mismatch. Regenerated with
--lockfile-only; no version changes.

Claude-Session: https://claude.ai/code/session_01RV8uiMYNhrAdTUzJ9fkpcp
2026-06-19 16:01:38 -04:00
teamchong 48f82ffee2 fix(dashboard): hero uses cache-weighted tokens (match Details/Saved)
The session hero computed (1 - sent / raw_count_tokens_baseline) — the same
cache-blind math just removed from the Details panel. It priced the all-text
baseline at full rate, ignoring that most of it would have been cheap
cache-reads, so a warm net-loss session showed a big "fewer tokens" win up top
while Details/Saved showed a loss. Two panels, one session, opposite signs.

- renderSessionSummaryFragment now reads baselineInputWeighted /
  actualInputWeighted (the same cache-weighted pair as Details + the Saved
  column) instead of rawActualTokens / rawBaselineTokens.
- Headline is input-only; dropped the output-on-both-sides lumping (output is
  never "sent" and only dampened the %).
- Honest labels: "effective tokens … after normal cache discounts"; still no $
  assumptions (0.1x/1.25x are token multipliers, not $/Mtok).
- types.ts: corrected stale field comments (raw fields are math-drawer only).
- +4 hero tests pinning direction to the weighted pair and that output can't
  move the headline %.

374 tests pass; typecheck + build clean.
2026-06-19 15:58:22 -04:00
teamchong c899d7d836 fix(dashboard): Details headline uses cache-weighted tokens (match row)
The Details panel divided the raw count_tokens baseline by sent tokens
(cache-blind), over-claiming "X% smaller" even when the cache-aware row
showed a net loss — the two panels could flatly contradict each other.

- dashboard.ts: feed baselineInputEff/actualInputEff/haveBaseline into
  ContextMapData (same weighted pair the recent row uses); push after
  the eff-tokens are computed so the breakdown matches.
- fragments.ts: headline divides weighted baseline by weighted actual,
  says "bigger" on a net loss, with a clarifying sub-line; no savings
  claim when the baseline probe did not resolve.
- +4 tests (context-map.test.ts) pinning headline direction to the row.

370 tests pass; typecheck + build clean.
2026-06-19 14:38:17 -04:00
teamchong 97d50513aa feat(openai): GPT-5.x family + Responses API support
- Generalize isPxpipeSupportedGptModel to the GPT-5 family (gpt-5, 5.x,
  -mini/-nano), default-on; [variant] tags stripped.
- OpenAI render switches to the 768px portrait strip (GPT_STRIP_COLS=152)
  so the API's mandatory shortest-side->768 downscale never shrinks our
  5px glyphs; downscale-free in both tile and patch regimes.
- Add OpenAI vision-token cost model (resolveVisionCost/openAIVisionTokens)
  and rewire the Chat Completions profitability gate to use it instead of
  the Anthropic 750px/token model.
- Add transformOpenAIResponses for the /v1/responses (Responses API) shape
  used by Codex: instructions + input items + flat tools -> rendered
  input_image parts; wire it into the proxy (was passthrough before).
- Tests: +12 (gate family, vision-token math incl. downscale path, chat +
  responses transforms). 366 passing; Anthropic path unchanged.

README intentionally not updated (GPT OCR fidelity unverified until tested).
2026-06-19 13:47:44 -04:00
teamchong ef9ddac58a feat(library): caller fidelity + recoverable channel, edge-safe; trim comments
- keepSharp: caller predicate pins exact-match-critical blocks (IDs, hashes,
  paths) as text, overriding the chars/token heuristic. Reported via
  info.keptSharpBlocks. Never forces imaging; throwing predicate = false.
- emitRecoverable: optional recovery map (block -> original text + provenance)
  so a stateful caller can re-inject or re-fetch imaged content. The recover
  half of the fidelity contract; documented mitigation for lossy recall.
- edge/Workers-safe: process.env read is typeof-guarded; @napi-rs/canvas moved
  to devDependencies (atlas is build-time only, runtime PNG encoder is pure-JS).
- comments: trimmed ~1,640 lines of essay/derivation/changelog narration across
  src/ (transform.ts -73%), keeping one-line rationale; deep math points to docs/.
- New exports/types in index.ts + library.ts; +12 tests (keep-sharp, recoverable).

Build clean; 354/354 tests pass.
2026-06-19 13:02:30 -04:00
teamchong 8eb035025a docs: lead messaging with token reduction, not dollar savings
Tokens are the durable invariant; the dollar figure is a pricing-dependent
consequence that evaporates if Anthropic reprices.

- README: headline + intro now lead with input-token reduction; the
  ~59-70% end-to-end bill is demoted to a downstream effect 'at current
  Fable list prices', with the dollar math pointed to the FAQ
- dashboard: stop claiming Claude's replies are 'never compressed' (false
  for any session past keepTail) — scope to latest messages + live output
2026-06-19 11:26:39 -04:00
teamchong cf4188625a docs: add HISTORY_CACHE_MODEL.md, conceptual companion to caching/savings
Captures why imaging conversation history stays cache-safe as a session
grows: the quantized collapseChunk boundary, the one-time cache-create
burn + amortization gate, the protectedPrefix slab anchor, and the
relocate-don't-add cache_control rule. Grounded in src/core/history.ts
and transform.ts; prose mirror of the verified code.
2026-06-19 11:26:39 -04:00
teamchong e5f5bc3d31 fix(node): exit cleanly on Ctrl+C during in-flight streams and idle keep-alive
server.close() waits for open connections to drain, so it never finishes while
the dashboard tab polls every 2s or while an SSE response is mid-stream - the
first Ctrl+C printed 'shutting down' but the process never exited.

Drop idle keep-alive sockets immediately, force-close anything still streaming
after a 1.5s grace, and make a second Ctrl+C an instant hard exit.
2026-06-19 09:34:35 -04:00
teamchong 13456812b1 redesign dashboard: light flame theme, plain-language hero, image/text transparency
Full rewrite of src/dashboard/fragments.ts (CSS + render functions + page
shell). Server JSON contracts, htmx endpoints, element IDs, and test-pinned
strings are all preserved.

- Light theme with flame-orange accent (no dark mode)
- One plain-language hero answer on top; all cc=1.25 + cr*0.10 formulas and
  counterfactual diagnostics moved behind a 'Show the math' drawer
- Image vs Text Breakdown panel: 'Became an image / Stayed as text' tabs plus
  an Image + Source inspector showing the verbatim text behind each page
- Recent table gains a color-coded 'Sent as: image/text' column
- Wide tables (#frag-recent, #frag-stats) and the latest-image inspector
  (#frag-latest) scroll inside their card instead of pushing the border out
2026-06-19 09:34:35 -04:00
teamchong de3f756f16 deps: patch 6 dependabot advisories (vite, undici, esbuild)
Bump dev/build tooling to patched versions and add pnpm overrides
for transitive deps. All advisories are Windows-specific or
dev-server issues; none touch the runtime proxy path.

- vite    8.0.13 -> ^8.0.16   (GHSA-fx2h-pf6j-xcff high, GHSA-v6wh-9g99-6wx3 moderate)
- esbuild 0.28.0 -> ^0.28.1   (GHSA-g7r4-m6w7-qqqr low)
- undici  override ^7.28.0    (GHSA-vmh5-mc38-953g high, GHSA-pr7r-676h-xcf6 moderate; via wrangler>miniflare)
- esbuild override ^0.28.1    (via wrangler>esbuild)

pnpm audit: No known vulnerabilities found.
typecheck clean, build ok, 342/342 tests pass.
2026-06-18 23:47:54 -04:00
teamchong 7af6c200c3 eval+demo: route harnesses through interactive cci.py; opus → claude-opus-4-8[1m]
eval:
- Add eval/lib/cci.py: drop-in interactive Claude session shim (pexpect/PTY)
  replacing headless `claude -p`. Drives the real TUI on Max-subscription auth
  (no ANTHROPIC_API_KEY), scrapes the reply + /context + /cost, and is
  argv/stdin/--output-format json compatible with the old call sites.
- Wire every call site to cci.py: gist-recall (run/run2/run3),
  needle-haystack (sweep/crux), gsm8k (bench), swe-bench (run_pilot),
  swe-bench-pro (rep_nav/run_pro), and eval/lib/anthropic-client.mjs.
- Isolation per call: --setting-sources project --strict-mcp-config
  --dangerously-skip-permissions. Recall evals keep --allowedTools Read;
  swe-bench keeps the full agentic toolset. Each trial is a fresh process.

demo:
- Resolve the `opus` shorthand to the full claude-opus-4-8[1m] model in
  cost-ab and effective-context a.sh/b.sh. setup.sh intentionally keeps the
  base id: the proxy scope gate strips [1m] from the request before matching,
  so [1m]-on-launch + base-in-scope is the correct pairing.

docs:
- FINDINGS.md: drop the 2026-06-16 Opus 4.8 re-test sections.
2026-06-18 23:43:37 -04:00
teamchong 74e2b97b1b docs: exact-recall escape hatch — route byte-exact subtasks to a non-Fable subagent 2026-06-18 21:34:03 -04:00
teamchong af1e838f0f chore: release 0.3.1 — Drive-hosted demo, keep preview thumbnail
Demo clip moved to Google Drive (committed copy was too low-res); README keeps the preview thumbnail linking to the Drive video. Restoring the thumbnail also un-breaks the demo image on the npm 0.3.0 page. Version 0.3.0 -> 0.3.1 + CHANGELOG.
2026-06-17 15:42:42 -04:00
teamchong 85aed799e5 docs(readme): drop blurry committed demo video, link to Drive instead
The recorded clip was too low-res to read; remove the 8.9MB mp4 + thumbnail from the tree and point the Demo section at the Drive copy instead.
2026-06-17 15:15:10 -04:00
teamchong a9ff92d3e1 docs(readme): add side-by-side A/B demo video (Opus 4.8)
Attach the recorded plain-vs-pxpipe demo (cost-ab fix + effective-context recall) plus a poster thumbnail, embedded in a new Demo section with an honest caption (Opus opt-in; demo 2 shows pxpipe surfacing the lossy-count limit rather than fabricating). Fable headline clip to follow when Fable is back.
2026-06-17 13:02:37 -04:00
teamchong fab35ed2d2 docs(readme): fix stale constants table + test count; note Opus opt-in
The library-use constants table still showed the pre-0.3.0 values (DENSE_CONTENT_CHARS_PER_IMAGE 5000, MAX_HEIGHT_PX 1568), contradicting the 'how it works' section and the shipped code. Updated to 1932 / 92160 + DENSE_CONTENT_COLS 384; test count 323->342; 'Fable-5 only' -> 'Fable-5 by default, Opus opt-in'.
2026-06-17 12:21:48 -04:00
teamchong d2d410ffc8 chore: scrub private data from repo; honest doc tweaks
- Remove eval/corpus/{sessions,text-blocks}.json from the repo — they are real captured transcripts (file paths, project names, message content). gitignore eval/corpus/; local copies kept for the eval scripts.

- gen_sweep.mjs: replace a hardcoded /Users/<name> absolute import with a relative path.

- README: 'holds ~92k readable chars' -> 'up to ~92k chars' (don't overclaim legibility at the new page density).

- CHANGELOG: note OCR legibility at the larger page size is not independently re-eval'd.
2026-06-17 12:08:16 -04:00
teamchong 716d9e0965 test: cap corpus L0 test to an 8MB per-file prefix
The real-corpus reflow check read whole transcript files; on a large ~/.claude/projects (multi-hundred-MB files) that blew the 30s timeout and aborted 'pnpm test' (hence prepublishOnly). Read only the first 8MB per file — enough for the per-file sample; a truncated trailing line just fails JSON.parse and is skipped. Corpus test now runs in ~5s.
2026-06-17 10:44:46 -04:00
teamchong dadee3b09e feat(demo): setup.sh takes a model arg; default Fable-only (Opus off)
setup.sh resolves a model arg (default fable) and sets PXPIPE_MODELS to Fable plus the chosen model, so the demo matches the production default (Opus OFF) and 'setup.sh opus' enables Opus end-to-end across setup/a.sh/b.sh. READMEs updated to pass the model to all three.
2026-06-17 10:07:32 -04:00
teamchong a64768bc4e chore: release 0.3.0
Version bump 0.2.1 -> 0.3.0 and CHANGELOG for the page-ceiling, dashboard
transparency, honest-savings, and code-review-fix work on this branch.
2026-06-17 10:01:33 -04:00
teamchong ad7ef7c1ac docs(demo): cost-ab + effective-context A/B demos with a model arg
Both demos default to Fable; pass a model as the first arg (a.sh opus, sonnet,
haiku, or a full claude-... id) to override -- run both columns on the same
model for a fair A/B. Adds eval/ab token-savings scripts.
2026-06-17 10:01:33 -04:00
teamchong 1cb62c1786 feat(dashboard): context-map transparency, flexible model chips, Opus default-off
- "How your context works" panel: per-request token flow (as-text -> real) plus
  the exact-char bucket breakdown of what became images and a gallery of every
  rendered page, opened via a "view" link on each recent-requests row.
- "compress models" chips are the union of a catalog (Fable 5, Opus 4.8/4.7,
  Sonnet 4.6, Haiku 4.5), the PXPIPE_MODELS env scope, and the active scope, so
  any env-enabled model stays toggleable (off <-> on); runtime-only override.
- Opus is OFF by default (Fable-5-only scope); opt-in via env or the chips.

Fixes from the code review:
- contextHistory cap aligned to the recent table (was 30 < 50, so older "view"
  links silently showed the latest request's breakdown); an evicted or
  unrecorded request now shows an explicit "no longer available" message.
- the session headline phrases a net loss as "N% more tokens" instead of
  rendering a garbled "-7% fewer tokens".
2026-06-17 10:01:33 -04:00
teamchong a9bda51210 feat(savings): honest negative accounting; drop the false >=0 clamp claim
Per-turn and per-session savings are the real baseline_eff - actual_eff with no
floor: a net-losing turn (cache_create-heavy image rewrite that costs more than
the text prefix it replaced) now reports the real loss instead of a fabricated
0. Updates baseline.ts and the caller comments that claimed a >=0 clamp the code
never applied -- the negative is intended and covered by tests/sessions.test.ts.
2026-06-17 10:01:33 -04:00
teamchong 8af9829872 feat(render): raise page ceiling to ~1932x1932 and align gate/paging geometry
Fable 5 / Opus 4.8 accept up to 2576px / 4784 visual tokens per image, but a
request with >20 images (pxpipe always sends many) is held to the stricter
<=2000px/side rule, so the real ceiling is ~1932x1932 (1928x1928 = 69x69 = 4761
tokens). MAX_HEIGHT_PX 1568->1932; dense tool/history pages now render at
DENSE_CONTENT_COLS=384 / DENSE_CONTENT_CHARS_PER_IMAGE=92160 (1928x1928 full
page) -- fewer image blocks at the same OCR-validated 5x8 cell. The static slab
is unchanged (313 cols / 1573x1280).

Also fixes a regression the code review caught: the break-even gate,
truncateForBudget, and estimateImageCount predicted against the slab geometry
(313 cols / 159 rows) while the dense renderer emits 384 cols / 240 rows, so
large tool_results were truncated far earlier than the 10-image cap required --
silently dropping output that would have rendered. A per-page char budget is
threaded through the prediction chain (defaults to READABLE so the slab and unit
tests are byte-identical) plus a denseGateGeometry helper, so the gate prices the
same page the renderer actually produces.
2026-06-17 10:01:33 -04:00
teamchong 514a4f0b74 feat(gate): config-driven model scope (PXPIPE_MODELS) + Opus 4.8 re-test
- applicability: isPxpipeSupportedModel reads PXPIPE_MODELS (comma-sep,
  default claude-fable-5 only) and strips bracket/variant tags like [1m];
  Opus becomes opt-in with no code change. Default behavior unchanged.
- FINDINGS: Opus 4.8 re-test - arithmetic 98/100 (-2pp), verbatim 6/15
  (was 93/100, 0/15): improved but still taxed -> default stays Fable-only.
- FINDINGS + eval/glyph-matrix/sweep: root cause of the read tax is per-glyph
  RESOLUTION, not font shape (confusions scale with px and vanish by 10x16).
  Opus needs ~4x glyph area to read reliably; the 1568px ceiling makes that
  break-even, so there is no profitable Opus operating point.
2026-06-16 12:22:55 -04:00
teamchong 50f3f1f86b docs: add prompt-caching alignment + honest savings-math doc
New docs/CACHING_AND_SAVINGS.md explains the two things that keep coming up:

1. How pxpipe stays aligned with Anthropic's prompt cache when it images the
   static prefix: split static/dynamic blocks first (keep drifting <env> etc.
   as cheap text, never imaged), then relocate (never add) the caller's
   cache_control onto the last image so the breakpoint anchors at the end of
   the stable slab. Covers the cache-friendly splice, the symmetric
   anti-flapping burn term, and the horizon-amortized history-collapse gate.

2. How savings are measured without double-counting the cache discount: both
   the real request and a count_tokens counterfactual of the original body are
   priced with identical cache rates at the same moment, so the discount
   cancels in the difference and only the token reduction survives.

Also reconcile the priorWarmTokens JSDoc in transform.ts: the burn term is
applied undivided, matching all three gate call sites and the symmetric
priorWarmImageTokens doc. The '/ N' was stale (the per-turn gate has no
horizon to divide by; the horizon scales the lifetime cost, not the burn).
2026-06-16 08:35:48 -04:00
teamchong be85f58d90 demo: pivot to smart-zone demo (plain Claude rots at large context, pxpipe stays sharp)
- generate.mjs builds one large context (filler logs + small state-tracking needle)
- README: two-column run (plain vs pxpipe), same prompt, compare answers + /context
- drop the e-commerce /context demo; context/ is generated, gitignored
2026-06-16 08:18:21 -04:00
teamchong 0277facc5d docs: reframe savings as workload-dependent snapshots; add demo + render-sizing doc
- README: cost savings now presented as measured snapshots (~59-70% end-to-end,
  ~72-74% compressed) bound to trace + workload, not a fixed 59%
- add demo/ (synthetic side-by-side /context demo: data, prompt, expected answer)
- add docs/RENDER_SIZING.md (image-sizing rules, rationale, commit history)
2026-06-15 22:33:31 -04:00
teamchong 7ee263c596 docs: drop glyph-matrix resume date (reader is fable-5, currently unavailable) 2026-06-14 17:27:35 -04:00
teamchong a10837dcdb docs: add roadmap - glyph legibility, effective context, context-rot hypotheses (all unmeasured) 2026-06-14 17:24:45 -04:00
teamchong 0ec9efe9ca docs: FINDINGS field observation (n=1) + glyph-matrix PLAN.md
- FINDINGS.md: dated 2026-06-12 entry logging in-the-wild verbatim
  misreads (7/10 exact, ~3.3% per-char, 2/3 silent) alongside flawless
  gist-tier recall in the same session. Explicitly anecdotal, n=1,
  non-reproducible — motivation only.
- eval/glyph-matrix/PLAN.md: public plan for the paused confusion-matrix
  experiment (Task #7) so the pending work is visible outside README,
  per the measured-numbers-only README rule.
2026-06-12 13:25:51 -04:00
teamchong 958b17357b Revert "docs: troubleshooting note for minimum-release-age install failures"
This reverts commit c865520657.
2026-06-12 06:23:21 -04:00
teamchong c865520657 docs: troubleshooting note for minimum-release-age install failures 2026-06-12 06:22:20 -04:00
teamchong 8bc1478406 v0.2.1: README scope corrections (docs-only) v0.2.1 2026-06-12 05:55:28 -04:00
teamchong 69eb96d17e docs: README scope wording - imaging covers system prompt + tool docs, not just old history 2026-06-12 05:40:57 -04:00
teamchong 5704c84237 docs: add real-world failure FAQ; scrub usernames/home paths from eval corpus 2026-06-11 17:17:19 -04:00
teamchong 62790e7371 docs: add FAQ - end-to-end bill math vs touched-only, measurement method, compression scope 2026-06-11 16:53:18 -04:00
teamchong d990faf646 eval(verbatim): expand to n=15 dense hex recall on Fable 5 - 13/15 vs Opus 0/15, both misses single-glyph confabulations 2026-06-11 16:48:12 -04:00
teamchong 7d62a4c715 eval(swe-bench-pro): expand to 19 pairs — ON 14/19 vs OFF 15/19, agree 18/19; navidrome 3/3 replication clears the ON loss 2026-06-11 15:47:54 -04:00
teamchong 156945977e eval(swe-bench-pro): 10-pair bench - ON 6/9 vs OFF 7/9, -61% per-request, receipts committed 2026-06-11 10:43:02 -04:00
teamchong 4eef1ec1a0 fix(eval/swe-bench-pro): verified proxy startup + atomic repo cache clone 2026-06-11 08:09:48 -04:00
teamchong b8e1e855bc feat(eval/swe-bench-pro): resumable 10-pair runner on bench-dedicated proxies
- ON 47823 / OFF 47824 with own PXPIPE_LOG files: operator session can
  never pollute bench measurement (separation by construction)
- resume = skip existing patch files; clean stop on quota errors
- Docker grading documented as proxy-free (no model calls in containers)
- README: mention Pro pilot alongside Lite
2026-06-11 08:05:06 -04:00
teamchong e98bf36387 fix(eval/swe-bench-pro): deconfound ON-arm numbers from operator session sharing port 47821 2026-06-11 05:00:01 -04:00
teamchong 78b2247a92 eval(swe-bench-pro): single-pair pilot - 1/1 both arms, -64% per-request, Rosetta gotcha documented 2026-06-11 04:56:06 -04:00
teamchong 8e8cb09223 docs: SWE-bench tables lead with per-request -65%, demote confounded run totals 2026-06-10 23:49:38 -04:00
teamchong 83764a18a0 docs: surface SWE-bench parity in honest-part section, link eval dir 2026-06-10 23:31:55 -04:00
teamchong 76542b4a3c eval(swe-bench): Lite pilot 10 paired instances — 10/10 resolved both arms, ON $27.27 vs OFF $53.61 (-49%)
- run_pilot.py harness (paired generation via proxy ON 47821 / OFF 47822)
- official swebench Docker grading via colima, reports + patches committed
- README.md results table, FINDINGS.md task-completion parity update
- repo README benchmark table gains SWE-bench row
2026-06-10 23:25:24 -04:00