Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
32ccbbd
Support request limit
pearmini Apr 13, 2026
0aa679f
Fix build
pearmini Apr 13, 2026
e2b7091
Fix multiple fetches
pearmini Apr 13, 2026
8fefacb
Fix duplicated types
pearmini Apr 13, 2026
a78983a
Restore package-lock.json
pearmini Apr 13, 2026
621c665
Remove useless code
pearmini Apr 13, 2026
8ca3f44
Fix pre role
pearmini Apr 13, 2026
e8223ef
Add termConfig and defaults
pearmini Apr 20, 2026
a3785ea
Validate in select time
pearmini Apr 21, 2026
92dbbb0
Fetch booking hisotry
pearmini Apr 28, 2026
3294944
Merge branch 'main' into request-limit
pearmini Apr 28, 2026
f4dff30
Merge branch 'main' into request-limit
pearmini May 13, 2026
3c88050
Remove origin for request limit
pearmini May 13, 2026
7a90c24
Use default timezone
pearmini May 13, 2026
d2e77a2
Restore schemaEditor to main
pearmini May 13, 2026
dadaf4c
Add tenant schema CLI backup to Firestore with dry-run local export
pearmini May 13, 2026
8d6f9dd
Update timezone to New York
pearmini May 13, 2026
6db3a38
perf(request-limits): bound the bookings query by earliest active window
rlho May 28, 2026
05b871a
Merge remote-tracking branch 'origin/main' into request-limit
rlho May 28, 2026
2043108
test(e2e): cover request-limits 429 path on /selectRoom
rlho May 28, 2026
91a9e86
Merge remote-tracking branch 'origin/main' into request-limit
rlho Jun 4, 2026
464d101
fix(request-limits): use schema.tenantId after tenant-schema migration
rlho Jun 4, 2026
e7b8b71
fix(merge): restore tenant-schema migration files dropped in prior co…
rlho Jun 4, 2026
2ffa4c2
Merge pull request #1403 from ITPNYU/request-limit
rlho Jun 4, 2026
49f8bf3
refactor(schema): remove legacy tenantSchema compat code (Phase 3 of …
rlho Jun 4, 2026
3e0376b
ci(firestore): drive index deploy from firestore.indexes.json
rlho Jun 4, 2026
292e19c
Merge pull request #1497 from ITPNYU/1238-phase3-remove-legacy-schema
rlho Jun 4, 2026
d0f9312
Merge pull request #1496 from ITPNYU/fix/request-limits-firestore-index
rlho Jun 4, 2026
31e5a1b
chore(deps-dev): bump vitest
dependabot[bot] Jun 9, 2026
113d863
feat(schema): migrate tenant resources to string resource IDs
n3xta Jun 15, 2026
f17032c
feat: add per-resource resource approvers
n3xta Jun 15, 2026
b15bcac
Revert "feat(schema): migrate tenant resources to string resource IDs"
n3xta Jun 15, 2026
a5609e3
Reapply "feat(schema): migrate tenant resources to string resource IDs"
n3xta Jun 15, 2026
082e101
ci: replace npm install --force with npm ci to fix ETXTBSY race condi…
Copilot Jun 15, 2026
25522cf
Merge pull request #1498 from ITPNYU/dependabot/npm_and_yarn/booking-…
rlho Jun 15, 2026
3f09828
fix(schema): normalize resource IDs in migration
n3xta Jun 15, 2026
6120d48
Merge pull request #1501 from ITPNYU/feat/tenant-resource-id-string
rlho Jun 15, 2026
32a31e6
chore(deps): bump the npm_and_yarn group across 2 directories with 3 …
dependabot[bot] Jun 15, 2026
d4e97d0
Merge branch 'feat/tenant-schema-cli-backup'
pearmini Jun 21, 2026
25fc876
Fix tenant schema resouce id
pearmini Jun 21, 2026
88af89d
Fix reset service status on edit
pearmini Jun 21, 2026
559df66
Merge remote-tracking branch 'origin/main' into feat/resource-approve…
n3xta Jun 22, 2026
36b1874
Fix booking end time modification
pearmini Jun 22, 2026
5d8d329
fix(resource-approvers): address review feedback
n3xta Jun 22, 2026
8249352
fix(resource-approvers): restore table UI
n3xta Jun 22, 2026
b1a95c6
fix(resource-approvers): tolerate default mui theme in tables
n3xta Jun 22, 2026
dc93f43
Delete crud files
nopivnick Jun 22, 2026
54d8ea7
Merge pull request #1512 from ITPNYU/delete-crud
nopivnick Jun 22, 2026
327f7f4
fix(resource-approvers): avoid theme palette assumptions
n3xta Jun 22, 2026
7f1cde6
Merge pull request #1509 from ITPNYU/fix/1506-template-tenant-schema-…
rlho Jun 22, 2026
5e1504a
Merge pull request #1510 from ITPNYU/fix/1486-reset-service-status-on…
rlho Jun 22, 2026
066a2ae
Merge pull request #1503 from ITPNYU/feat/resource-approvers-per-reso…
rlho Jun 26, 2026
980b9f2
Merge pull request #1505 from ITPNYU/dependabot/npm_and_yarn/booking-…
rlho Jun 27, 2026
953541e
Merge branch 'main' into fix/1389-booking-end-time-modification
pearmini Jun 28, 2026
66d5c8b
Fix unit test ResizeObserver mock for Vitest 4 CI.
pearmini Jun 28, 2026
4f50367
feat: add booking maintenance mode
n3xta Jun 28, 2026
4632427
Revert "feat: add booking maintenance mode"
n3xta Jun 28, 2026
1aae1f7
Merge pull request #1511 from ITPNYU/fix/1389-booking-end-time-modifi…
rlho Jun 29, 2026
186d9e8
chore: remove Google Apps Script era leftovers
rlho Jul 2, 2026
f5bed80
security: stop exposing server secrets through next.config env
rlho Jul 2, 2026
0ef2eb1
Merge pull request #1516 from ITPNYU/chore/remove-gas-leftovers
rlho Jul 4, 2026
3a0f0a6
chore(deps): bump the npm_and_yarn group across 1 directory with 3 up…
dependabot[bot] Jul 4, 2026
8c9a4e7
Merge pull request #1517 from ITPNYU/security/stop-shipping-secrets
rlho Jul 4, 2026
8530c17
ci: add Claude PR review workflow
rlho Jul 4, 2026
5b4241b
Merge pull request #1520 from ITPNYU/dependabot/npm_and_yarn/booking-…
rlho Jul 4, 2026
3f190d4
chore(deps): bump js-yaml
dependabot[bot] Jul 4, 2026
f10e477
ci: harden claude-review trust boundary
rlho Jul 4, 2026
9fafc4c
ci: run submit script from base ref and gate on same-repo PRs
rlho Jul 4, 2026
dd291ac
ci: sanitize environment for the trusted submit step
rlho Jul 4, 2026
b64a0d1
Merge pull request #1522 from ITPNYU/dependabot/npm_and_yarn/booking-…
rlho Jul 4, 2026
bc461f1
ci: add reopened trigger and document allowlist residual risk
rlho Jul 4, 2026
a0f51fb
Merge pull request #1521 from ITPNYU/ci/claude-pr-review
rlho Jul 4, 2026
3042914
ci: remove claude-review bootstrap fallback, fail closed
rlho Jul 4, 2026
91ba95d
ci: give Claude PR review read access to the checkout for cross-file …
rlho Jul 6, 2026
38def98
Merge pull request #1526 from ITPNYU/ci/claude-review-remove-bootstrap
rlho Jul 6, 2026
6c16c05
Merge pull request #1528 from ITPNYU/ci/claude-review-repo-read
rlho Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
5 changes: 0 additions & 5 deletions .clasp.json

This file was deleted.

2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<!-- If schema changed, list affected tenants and what changed.
Example:
- mc: added `showCleaning` flag
- itp: added new resource (roomId 999)
- itp: added new resource (resourceId "999")
-->

## Checklist
Expand Down
208 changes: 208 additions & 0 deletions .github/scripts/submit_review.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""Submit the Claude review payload as one PR review, resilient to inline
comments GitHub cannot anchor to the diff.

The model writes ``/tmp/review.json`` with inline comments keyed to new-file
line numbers. GitHub's create-review API rejects the WHOLE review (HTTP 422,
"Line could not be resolved") if any single comment's line falls outside the
PR's diff hunks. To stay green without dropping findings, this script:

1. Computes the set of commentable RIGHT-side lines from ``gh pr diff``.
2. Keeps inline comments whose (path, line) is anchorable; folds the rest
into the review body so the finding still shows up.
3. POSTs once. On any residual failure, retries body-only, then gives up
gracefully -- a review-infra hiccup must not block the PR.

Trust boundary: this is the trusted step. The endpoint is hardcoded here and
the model never holds ``gh api``; a malicious diff can only influence comment
*text* (which already lands in the review body either way), never which API
call runs.
"""
from __future__ import annotations

import json
import os
import re
import subprocess

REVIEW_PATH = "/tmp/review.json"


def parse_commentable_lines(diff: str) -> dict[str, set[int]]:
"""Map new-file path -> set of RIGHT-side line numbers inside diff hunks.

GitHub allows a RIGHT-side comment on added (``+``) and context (`` ``)
lines within a hunk; removed (``-``) lines have no new-file line. Pure
function over unified-diff text so it can be unit-tested without network.
"""
valid: dict[str, set[int]] = {}
path: str | None = None
newln = 0
in_hunk = False
for line in diff.splitlines():
# A new file section resets state so inter-file metadata
# (``diff --git``, ``index``, ``--- ``/``+++ `` headers) is never
# mistaken for hunk content.
if line.startswith("diff --git"):
path, in_hunk = None, False
continue
if not in_hunk:
if line.startswith("+++ "):
p = line[4:].strip()
if p.startswith("b/"):
p = p[2:]
path = None if p == "/dev/null" else p
if path is not None:
valid.setdefault(path, set())
elif line.startswith("@@"):
m = re.search(r"\+(\d+)", line)
newln = int(m.group(1)) if m else 0
in_hunk = True
continue
# Inside a hunk.
if line.startswith("@@"): # next hunk of the same file
m = re.search(r"\+(\d+)", line)
newln = int(m.group(1)) if m else 0
continue
if path is None:
continue
if line.startswith("+"): # added line (commentable on RIGHT)
valid[path].add(newln)
newln += 1
elif line.startswith(" "): # context line (commentable on RIGHT)
valid[path].add(newln)
newln += 1
# ``-`` (removed, no RIGHT line) and ``\`` ("No newline") consume nothing.
return valid


def post(repo: str, pr: str, payload: dict) -> tuple[int, str]:
# Review posting is advisory and must never crash the job — surface a
# missing/broken gh binary as a failed attempt, not an unhandled exception.
try:
proc = subprocess.run(
["gh", "api", "--method", "POST",
f"/repos/{repo}/pulls/{pr}/reviews", "--input", "-"],
input=json.dumps(payload), capture_output=True, text=True,
)
except OSError as exc:
return 1, f"could not invoke gh: {exc}"
return proc.returncode, (proc.stdout + proc.stderr).strip()


def partition_comments(
comments: list[dict], valid: dict[str, set[int]]
) -> tuple[list[dict], list[dict]]:
"""Split comments into (anchorable-inline, must-fold-into-body).

A comment is anchorable when it targets the RIGHT side and both its
``line`` and (for a multi-line span) ``start_line`` land on a commentable
line of the file in the diff.
"""
kept, folded = [], []
for c in comments:
p, ln, sl = c.get("path"), c.get("line"), c.get("start_line")
anchorable = (
c.get("side", "RIGHT") == "RIGHT"
and p in valid and ln in valid[p]
and (sl is None or sl in valid[p])
)
(kept if anchorable else folded).append(c)
return kept, folded


def _loc(c: dict) -> str:
return f"{c.get('path')}:{c.get('line')}"


def main() -> None:
pr = os.environ["PR_NUMBER"]
repo = os.environ["REPO"]
# The model occasionally writes a /tmp/review.json that isn't valid JSON
# (e.g. an unescaped quote or newline inside a comment body), which made
# ``json.load`` raise and fail the whole job. Review posting is advisory
# and must not block the PR -- mirror the graceful give-up the submit
# fallbacks already use: log loudly and return. Re-running the review
# regenerates the file.
try:
with open(REVIEW_PATH) as f:
review = json.load(f)
except (OSError, json.JSONDecodeError) as exc:
print(
f"warning: could not read/parse {REVIEW_PATH} ({exc}); "
"skipping review submit, not blocking the PR."
)
return
if not isinstance(review, dict):
print(
f"warning: {REVIEW_PATH} is not a JSON object "
f"(got {type(review).__name__}); skipping review submit, "
"not blocking the PR."
)
return
# review.json is untrusted model output: the review verdict is pinned to
# COMMENT (never APPROVE/REQUEST_CHANGES) and commit_id comes from the
# workflow env, so an injected payload can only influence comment text.
raw_comments = review.get("comments")
comments = (
[c for c in raw_comments if isinstance(c, dict)]
if isinstance(raw_comments, list)
else []
)
body = review.get("body")
body = body if isinstance(body, str) else ""
base = {"event": "COMMENT"}
head_sha = os.environ.get("HEAD_SHA")
if head_sha:
base["commit_id"] = head_sha

valid: dict[str, set[int]] = {}
try:
diff = subprocess.run(
["gh", "pr", "diff", pr], capture_output=True, text=True, check=True
).stdout
valid = parse_commentable_lines(diff)
except Exception as exc: # diff fetch/parse failed -> fall back to body-only
print(f"warning: could not compute diff lines ({exc}); folding all inline comments")

kept, folded = partition_comments(comments, valid)

# GitHub's create-review API requires start_side when start_line is set;
# a comment that omits it would 422 the whole review.
for c in kept:
if c.get("start_line") is not None:
c.setdefault("start_side", "RIGHT")

def fold_into(text: str, items: list[dict], heading: str) -> str:
if not items:
return text
out = text + f"\n\n---\n**{heading}:**\n"
for c in items:
out += f"\n- `{_loc(c)}` -- {c.get('body', '').strip()}"
return out

# Attempt 1: anchorable comments inline, the rest folded into the body.
body1 = fold_into(body, folded, "Findings that could not be anchored to the diff")
rc, out = post(repo, pr, {**base, "body": body1, "comments": kept})
if rc == 0:
print(f"Posted review: {len(kept)} inline, {len(folded)} folded into body.")
return
print(f"Inline submit failed (rc={rc}); falling back to body-only.\n{out}")

# Attempt 2: everything in the body, no inline anchors at all.
body2 = fold_into(body1, kept, "Inline findings (anchoring unavailable)")
rc, out = post(repo, pr, {**base, "body": body2 or "Review produced no anchorable findings.",
"comments": []})
if rc == 0:
print("Posted body-only review.")
return

# A body-only COMMENT review needs no line resolution, so this is an
# auth/rate-limit/transport problem, not the 422 we set out to fix.
# Log loudly but do not fail the job: review posting is advisory and must
# not block the PR.
print(f"warning: review submit failed entirely (rc={rc}); not blocking the PR.\n{out}")


if __name__ == "__main__":
main()
Loading
Loading