# Calendar reconciliation — local source

Download `calendar-reconciliation.mjs` alongside this README. Import
`buildCalendarReconciliation(expectedText, scheduledText)` in a JavaScript
ES-module environment. It returns the same structured result as the browser
core: `{ draft, checked, issues, counts, findings }`. Nothing is printed or sent
unless your calling code does that. The module exports only this function.

Manually prepare two literal TSV lists with exactly four columns per row:
stable booking ID, resource/person, start timestamp, end timestamp. This is not
a native Calendar export or ICS parser. Use the same stable IDs in both lists;
prepare separate identifiable recurrence instances yourself. No header,
multiline cells, control characters, blank internal lines or extra columns.
CRLF/CR become LF and one final newline is allowed. Expected must have at least
one booking; scheduled may be an empty string. IDs and resources must be
nonblank and remain literal, including spaces and case. IDs must be unique
within each list; resources are not normalized or matched by aliases.

Timestamps use YYYY-MM-DDTHH:mm[:ss[.SSS]] with Z or an explicit ±HH:mm offset.
Dates must be valid, with local and UTC years 0001–9999; offsets range from
-14:00 to +14:00. Unknown offset -00:00 is rejected. End must be later than
start. Times compare as UTC instants; no timezone or daylight-saving assumption
is inferred. Touching intervals do not conflict. Only overlapping scheduled
intervals for the same exact resource are checked, including unexpected rows.

Limits: 100,000 characters and 500 rows per list, at most 1,000 scheduled
conflict pairs, and 300,000 draft characters. Invalid inputs or exceeded limits
throw before any result is returned; nothing is silently truncated. If batching,
retain bookings crossing the time-window boundaries so conflicts are not lost.

`checked` counts both lists' rows. `issues` sums missing, unexpected, changed
bookings and conflicting pairs; it is not a count of distinct bookings.
`normalizedMatches` is the subset of unchanged bookings whose timestamp text
differs while the UTC instants match. `findings` includes unchanged bookings and
retains original row references, literal cells and UTC timestamps. Draft quoted
values use JSON escaping. Review literal formula-like text before pasting it
into software that interprets formulas.

## Executable fictional example

Save the module and run this code as an ES module from the same directory.
The expected list has three bookings. The scheduled list preserves A using a
different offset spelling, moves B, omits C and adds X overlapping A.

```js
import { buildCalendarReconciliation } from './calendar-reconciliation.mjs';
const expected = [
  'A\tRoom Blue\t2026-10-07T09:00-04:00\t2026-10-07T10:00-04:00',
  'B\tRoom Green\t2026-10-07T15:00Z\t2026-10-07T16:00Z',
  'C\tRoom Gold\t2026-10-07T17:00Z\t2026-10-07T18:00Z',
].join('\n');
const scheduled = [
  'A\tRoom Blue\t2026-10-07T13:00Z\t2026-10-07T14:00Z',
  'B\tRoom Green\t2026-10-07T16:00Z\t2026-10-07T17:00Z',
  'X\tRoom Blue\t2026-10-07T13:30Z\t2026-10-07T14:30Z',
].join('\n');
const result = buildCalendarReconciliation(expected, scheduled);
console.log(JSON.stringify(result.counts));
console.log(`Checked: ${result.checked}; issues: ${result.issues}`);
console.log(result.draft.split('\n')[1]);
// result.draft is the complete reviewable text; result.findings holds evidence.
```

Expected exact console output:

```text
{"expected":3,"scheduled":3,"missing":1,"unexpected":1,"changed":1,"unchanged":1,"normalizedMatches":1,"conflicts":1}
Checked: 6; issues: 4
Summary: 3 expected; 3 scheduled; 1 missing; 1 unexpected; 1 changed; 1 unchanged (1 with equivalent timestamp spellings); 1 scheduled conflict pairs.
```

## Manual review and privacy

Check that both lists cover the same calendars and date window. Verify stable
IDs, resource aliases, offsets, recurrence instances, cancellations, attendees
and permissions in the real sources. Confirm missing, unexpected and changed
bookings before manually adding, deleting or editing events. Resolve actual
conflicts, refresh both lists and rerun. A matching report cannot establish
coverage of omitted calendars/events, attendee availability or actual changes.
Review the complete draft before sharing it.

The dependency-free module has no network, filesystem, process, secret access,
telemetry or sends. It does not access Calendar, create/change events or send
invitations. The caller controls input handling: an agent, logger or surrounding
app may retain or transmit data. Choose an appropriate local environment for
private lists; the module cannot make an external agent environment private.
This executable example is fictional QA. Source availability and successful QA
are not independent agent execution, customer completion, repeat use or demand.
No installation or paid outcome is claimed.

## License

MIT License

Copyright (c) 2026 QuasiLife

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
