Skip to content

[codex] add calendar workflow foundations#157

Merged
BASIC-BIT merged 1 commit into
mainfrom
codex/calendar-workflows
Jul 3, 2026
Merged

[codex] add calendar workflow foundations#157
BASIC-BIT merged 1 commit into
mainfrom
codex/calendar-workflows

Conversation

@BASIC-BIT

Copy link
Copy Markdown
Owner

Summary

  • add shared public ICS feed serialization alongside the existing single-event export
  • add review-only Google Calendar import staging validators, schema tables, document creation helper, and publication blockers
  • document the #138 calendar workflow foundation and remaining non-goals

Validation

  • node --import tsx --test tests/web/calendar-ics.test.ts tests/backend/event-calendar-imports.test.ts
  • node --import tsx --test tests/backend/**/*.test.ts
  • node_modules\.bin\tsc.CMD --noEmit --project convex\tsconfig.json
  • node_modules\.bin\eslint.CMD src\lib\calendar\ics.ts from apps/web
  • node_modules\.bin\next.CMD typegen; node scripts\ensure-next-type-stubs.mjs; node_modules\.bin\tsc.CMD --noEmit --incremental false from apps/web
  • node_modules\.bin\markdownlint-cli2.CMD docs\planning\calendar-integration.md docs\backend\event-schema.md docs\planning\dependency-map.md
  • node scripts/run-convex-local.mjs dev --local --once --run health:status --tail-logs disable
  • git diff --check

Part of #138.

@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vr-dex-web Ready Ready Preview, Comment Jul 3, 2026 12:06pm

Request Review

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Storybook Image Diff

Outcome: success
Run: https://github.com/BASIC-BIT/VRDex/actions/runs/28659477717
Artifact: storybook-image-diff

Changed Storybook baselines: none in this PR.

This check compares design-system component screenshots against committed baselines. Inline images show only added or modified Storybook baseline PNGs.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Storybook Component Screenshot Preview

Outcome: success
Run: https://github.com/BASIC-BIT/VRDex/actions/runs/28659477717
Artifact: storybook-component-preview

Screenshots: primitive component stories captured on desktop and mobile.

This lane is separate from full-route Playwright screenshots and focuses on design-system component regressions.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Playwright Hosted Data-Flow

Outcome: success
Target: https://staging.vrdex.net
Hosted extended profile flow: enabled
Hosted auth helpers: enabled
Hosted adapter helpers: enabled
Run: https://github.com/BASIC-BIT/VRDex/actions/runs/28659477717
Artifact: playwright-hosted-data-flow

This optional check runs the mutation-backed profile flow against a configured hosted dev/staging target with isolated E2E test data.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Playwright Data-Flow Preview

Outcome: success
Run: https://github.com/BASIC-BIT/VRDex/actions/runs/28659477717
Artifact: playwright-data-flow

Captured flow:

  • test-gated profile submission form
  • gated helper rejection without the Playwright token
  • Convex profile creation
  • submission success state
  • public profile page readback
  • discovery search readback

Artifacts include screenshots, traces, and recorded video for the flow run.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Playwright Image Diff

Outcome: success
Run: https://github.com/BASIC-BIT/VRDex/actions/runs/28659477717
Artifact: playwright-image-diff

Changed screenshot baselines: none in this PR.

This check compares public route screenshots against committed baselines. Inline images show only added or modified baseline PNGs.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Playwright Public Screenshot Preview

Outcome: success
Run: https://github.com/BASIC-BIT/VRDex/actions/runs/28659477717
Artifact: playwright-public-preview

Screenshots: all public route checks passed on desktop and mobile.

Full screenshot set is available in the artifact. Pixel diff baselines are handled by the separate Playwright Image Diff check.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

@BASIC-BIT BASIC-BIT marked this pull request as ready for review July 3, 2026 12:13
Copilot AI review requested due to automatic review settings July 3, 2026 12:14
@BASIC-BIT BASIC-BIT merged commit 350e2d6 into main Jul 3, 2026
23 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds foundations for calendar export/import workflows: a shared public ICS serializer that now supports multi-event public feeds, plus a Google Calendar import “review staging” layer in Convex (tables, normalizer, document creation helper, and publication blockers), with supporting docs and tests.

Changes:

  • Add createPublicEventFeedIcs to serialize a public ICS feed containing multiple safe VEVENT entries.
  • Introduce Convex schema + helpers for Google Calendar import staging (normalized candidates/fields + publication guardrails).
  • Document the implemented #138 foundations and explicitly call out remaining non-goals/follow-on work.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/web/calendar-ics.test.ts Adds coverage for multi-event public feed ICS serialization.
tests/backend/event-calendar-imports.test.ts New tests for Google Calendar normalization, staging inserts, and publication blockers.
docs/planning/dependency-map.md Records that #138 now has implemented foundations.
docs/planning/calendar-integration.md Updates the calendar direction doc to reflect implemented export + import-staging foundations.
docs/backend/event-schema.md Documents the new calendar import/export slice and staging table responsibilities.
convex/schema.ts Adds staging tables (eventImportBatches, eventImportCandidates, eventImportCandidateFields) + indexes.
convex/_generated/api.d.ts Exposes the new _eventCalendarImports module in generated API types.
convex/_eventCalendarImports.ts Implements validators/types, Google Calendar normalization, staging document writer, and publication blockers.
apps/web/src/lib/calendar/ics.ts Adds public feed ICS serialization and a shared VEVENT line helper.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +284 to +290
for (const match of value?.match(HTTPS_URL_PATTERN) ?? []) {
const url = requireHttpsUrl(match.replace(/[),.;]+$/g, ""), "Imported event link");

if (url !== undefined && !seen.has(url)) {
seen.add(url);
links.push(url);
}
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds calendar workflow foundations for public exports and reviewed Google Calendar imports. The main changes are:

  • Shared public ICS serialization for single events and selected event feeds.
  • Convex staging tables for Google Calendar import batches, candidates, and candidate fields.
  • Google Calendar normalization, staging document creation, and publication blocker helpers.
  • Tests and planning docs for the #138 import/export foundation.

Confidence Score: 4/5

Mostly safe, with one contained publication-guard bug to fix before relying on import publication checks.

Public ICS export and staging writes are well-scoped and covered by tests. The new publication blocker logic omits rejected field states, so rejected imported facts can pass the new gate.

convex/_eventCalendarImports.ts; tests/backend/event-calendar-imports.test.ts

T-Rex T-Rex Logs

What T-Rex did

  • Reproduced a Node test scenario for getEventImportPublicationBlockers with an approved batch, accepted review_pending candidate, and a public SAFE title field marked rejected, which resulted in blockers being returned as an empty array.
  • Ran the calendar-focused tests; the logs report 12 pass and 0 fail.
  • Ran the backend tests; the logs report 158 pass and 0 fail.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
apps/web/src/lib/calendar/ics.ts Adds shared public feed ICS serialization using the existing safe public-event field contract and URL validation.
convex/_eventCalendarImports.ts Adds Google Calendar import normalization, staging document creation, and publication blockers; rejected fields are not blocked from publication.
convex/schema.ts Adds Convex staging tables and indexes for import batches, candidates, and candidate fields.
tests/backend/event-calendar-imports.test.ts Covers normalization, staging writes, and publication blockers, but misses the rejected-field publication case.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant PublicProjection as Public event projection
participant Ics as ICS serializer
participant CalendarClient as Calendar client
participant GoogleInput as Google Calendar import input
participant Normalizer as Import normalizer
participant Convex as Convex staging tables
participant Review as Later review/publication flow

PublicProjection->>Ics: public events + feed/event URLs
Ics->>Ics: validate HTTP URLs and serialize VEVENTs
Ics-->>CalendarClient: public VCALENDAR feed

GoogleInput->>Normalizer: batch metadata + Google events
Normalizer->>Normalizer: validate timestamps, HTTPS source URLs, public/private fields
Normalizer->>Convex: insert eventImportBatches
Normalizer->>Convex: insert eventImportCandidates
Normalizer->>Convex: insert eventImportCandidateFields
Review->>Convex: read batch, candidate, fields
Review->>Review: evaluate publication blockers before canonical event publication
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant PublicProjection as Public event projection
participant Ics as ICS serializer
participant CalendarClient as Calendar client
participant GoogleInput as Google Calendar import input
participant Normalizer as Import normalizer
participant Convex as Convex staging tables
participant Review as Later review/publication flow

PublicProjection->>Ics: public events + feed/event URLs
Ics->>Ics: validate HTTP URLs and serialize VEVENTs
Ics-->>CalendarClient: public VCALENDAR feed

GoogleInput->>Normalizer: batch metadata + Google events
Normalizer->>Normalizer: validate timestamps, HTTPS source URLs, public/private fields
Normalizer->>Convex: insert eventImportBatches
Normalizer->>Convex: insert eventImportCandidates
Normalizer->>Convex: insert eventImportCandidateFields
Review->>Convex: read batch, candidate, fields
Review->>Review: evaluate publication blockers before canonical event publication
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
convex/_eventCalendarImports.ts:552-554
**Rejected fields pass publication**
`getEventImportPublicationBlockers` only blocks `unreviewed` and `needs_correction`, so a candidate with `reviewState: "accepted"`, `publicationState: "review_pending"`, and a public field marked `"rejected"` returns no blocker. That lets a reviewer-rejected imported fact proceed to the later publication step despite the field-level review state recording it as rejected; add a rejected-field blocker or require every publishable field to be `accepted`.

Reviews (1): Last reviewed commit: "Add calendar workflow foundations" | Re-trigger Greptile

Comment on lines +552 to +554
if (field.reviewState === "needs_correction") {
blockers.add("field_needs_correction");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Rejected fields pass publication
getEventImportPublicationBlockers only blocks unreviewed and needs_correction, so a candidate with reviewState: "accepted", publicationState: "review_pending", and a public field marked "rejected" returns no blocker. That lets a reviewer-rejected imported fact proceed to the later publication step despite the field-level review state recording it as rejected; add a rejected-field blocker or require every publishable field to be accepted.

Artifacts

Repro: generated failing Node test for rejected public import field publication blockers

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: failing test output showing rejected public field returns empty blockers

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: convex/_eventCalendarImports.ts
Line: 552-554

Comment:
**Rejected fields pass publication**
`getEventImportPublicationBlockers` only blocks `unreviewed` and `needs_correction`, so a candidate with `reviewState: "accepted"`, `publicationState: "review_pending"`, and a public field marked `"rejected"` returns no blocker. That lets a reviewer-rejected imported fact proceed to the later publication step despite the field-level review state recording it as rejected; add a rejected-field blocker or require every publishable field to be `accepted`.

How can I resolve this? If you propose a fix, please make it concise.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d9e243405

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

): { timestamp: number; timezone?: string; allDay: boolean } {
if (value?.dateTime !== undefined) {
return {
timestamp: requireIsoTimestamp(value.dateTime, fieldName),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Google timeZone when dateTime has no offset

When a valid Google event supplies dateTime like 2026-06-14T15:00:00 together with timeZone: "America/Los_Angeles", this parses the string in the server's local timezone instead of converting from the supplied IANA timezone, so the candidate can be staged at 15:00Z rather than 22:00Z. That corrupts review data for Google events using the offsetless+timeZone form; parse these with the provided timezone or reject offsetless values.

Useful? React with 👍 / 👎.

sourceLabel: string,
fallbackTimeZone: string | undefined,
): NormalizedGoogleCalendarImportCandidate {
const externalEventId = normalizeInlineText(event.id, "", EVENT_TEXT_MAX_LENGTH);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve full Google event IDs

Using normalizeInlineText here truncates the opaque Google event id to 240 chars even though Google allows event ids up to 1024 chars. For valid long ids, two events with the same 240-char prefix collapse to the same stored externalEventId, breaking later update/cancellation matching; preserve the full id or explicitly reject over-length identifiers.

Useful? React with 👍 / 👎.

}

const sourceUrl = requireHttpsUrl(event.htmlLink, "Google Calendar event URL");
const start = parseGoogleCalendarTime(event.start, "Event start", fallbackTimeZone);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stage Google cancellation tombstones without start times

For cancelled events returned by an incremental or showDeleted Google import, Google may send a cancellation tombstone with only identifiers and no start. This call then throws before cancellationState is recorded, so a single deleted recurring instance can abort the whole batch instead of being staged or skipped for review; handle status === "cancelled" before requiring start/end fields.

Useful? React with 👍 / 👎.

BASIC-BIT added a commit that referenced this pull request Jul 3, 2026
Address calendar import review feedback

Follow-up to #157 / #138.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants