skilldrop-cli 0.16.6 → 0.16.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/catalogue.json +11 -1
- package/model-routing.json +16 -0
- package/package.json +1 -1
- package/packs/ai-engineering/skills/ai-usage-policy/evals/evals.json +30 -0
- package/packs/ai-engineering/skills/ai-usage-policy/evals/files/2/data-handling-sheet.md +18 -0
- package/packs/ai-engineering/skills/ai-usage-policy/evals/files/2/it-notes.md +31 -0
- package/packs/ai-engineering/skills/ai-usage-policy/evals/files/2/md-email.md +29 -0
- package/packs/api-builder/skills/tool-use-schema-writer/evals/evals.json +29 -0
- package/packs/api-builder/skills/tool-use-schema-writer/evals/files/2/book_appointment.ts +64 -0
- package/packs/career/loops/apply/LOOP.md +97 -0
- package/packs/career/loops/apply/evals/eval_queries.json +9 -0
- package/packs/career/loops/apply/evals/evals.json +27 -0
- package/packs/career/loops/apply/loop.json +52 -0
- package/packs/career/pack.json +77 -0
- package/packs/career/skills/application-form-answers/SKILL.md +146 -0
- package/packs/career/skills/application-form-answers/evals/eval_queries.json +34 -0
- package/packs/career/skills/application-form-answers/evals/evals.json +103 -0
- package/packs/career/skills/application-form-answers/evals/files/1/form.md +17 -0
- package/packs/career/skills/application-form-answers/evals/files/1/posting.md +20 -0
- package/packs/career/skills/application-form-answers/evals/files/1/resume.md +32 -0
- package/packs/career/skills/application-form-answers/evals/files/2/form.md +15 -0
- package/packs/career/skills/application-form-answers/evals/files/2/posting.md +21 -0
- package/packs/career/skills/application-form-answers/evals/files/2/resume.md +32 -0
- package/packs/career/skills/application-form-answers/evals/files/4/form.md +14 -0
- package/packs/career/skills/application-form-answers/evals/files/4/posting.md +25 -0
- package/packs/career/skills/application-form-answers/evals/files/4/resume.md +29 -0
- package/packs/career/skills/application-form-answers/evals/files/5/form.md +15 -0
- package/packs/career/skills/application-form-answers/evals/files/5/posting.md +18 -0
- package/packs/career/skills/application-form-answers/evals/files/5/resume.md +23 -0
- package/packs/career/skills/application-form-answers/evals/files/6/form.md +11 -0
- package/packs/career/skills/application-form-answers/evals/files/6/posting.md +28 -0
- package/packs/career/skills/application-form-answers/evals/files/6/resume.md +35 -0
- package/packs/career/skills/application-form-answers/evals/files/7/form.md +11 -0
- package/packs/career/skills/application-form-answers/evals/files/7/posting.md +19 -0
- package/packs/career/skills/application-form-answers/evals/files/7/resume.md +26 -0
- package/packs/career/skills/application-form-answers/manifest.json +31 -0
- package/packs/career/skills/cover-letter/SKILL.md +132 -0
- package/packs/career/skills/cover-letter/evals/eval_queries.json +34 -0
- package/packs/career/skills/cover-letter/evals/evals.json +100 -0
- package/packs/career/skills/cover-letter/evals/files/1/posting.md +28 -0
- package/packs/career/skills/cover-letter/evals/files/1/resume.md +35 -0
- package/packs/career/skills/cover-letter/evals/files/2/posting.md +20 -0
- package/packs/career/skills/cover-letter/evals/files/2/resume.md +32 -0
- package/packs/career/skills/cover-letter/evals/files/4/posting.md +19 -0
- package/packs/career/skills/cover-letter/evals/files/4/resume.md +26 -0
- package/packs/career/skills/cover-letter/evals/files/5/posting.md +21 -0
- package/packs/career/skills/cover-letter/evals/files/5/resume.md +32 -0
- package/packs/career/skills/cover-letter/evals/files/6/posting.md +25 -0
- package/packs/career/skills/cover-letter/evals/files/6/resume.md +29 -0
- package/packs/career/skills/cover-letter/evals/files/7/posting.md +18 -0
- package/packs/career/skills/cover-letter/evals/files/7/resume.md +23 -0
- package/packs/career/skills/cover-letter/manifest.json +37 -0
- package/packs/career/skills/cover-letter/scripts/claim_check.py +250 -0
- package/packs/career/skills/job-fit-analysis/SKILL.md +152 -0
- package/packs/career/skills/job-fit-analysis/evals/eval_queries.json +34 -0
- package/packs/career/skills/job-fit-analysis/evals/evals.json +102 -0
- package/packs/career/skills/job-fit-analysis/evals/files/1/posting.md +28 -0
- package/packs/career/skills/job-fit-analysis/evals/files/1/resume.md +35 -0
- package/packs/career/skills/job-fit-analysis/evals/files/2/posting.md +20 -0
- package/packs/career/skills/job-fit-analysis/evals/files/2/resume.md +32 -0
- package/packs/career/skills/job-fit-analysis/evals/files/4/posting.md +25 -0
- package/packs/career/skills/job-fit-analysis/evals/files/4/resume.md +29 -0
- package/packs/career/skills/job-fit-analysis/evals/files/5/posting.md +18 -0
- package/packs/career/skills/job-fit-analysis/evals/files/5/resume.md +23 -0
- package/packs/career/skills/job-fit-analysis/evals/files/6/linkedin-about.txt +5 -0
- package/packs/career/skills/job-fit-analysis/evals/files/6/posting.md +25 -0
- package/packs/career/skills/job-fit-analysis/evals/files/6/resume.md +35 -0
- package/packs/career/skills/job-fit-analysis/evals/files/7/resume.md +32 -0
- package/packs/career/skills/job-fit-analysis/manifest.json +31 -0
- package/packs/career/skills/resume-tailor/SKILL.md +144 -0
- package/packs/career/skills/resume-tailor/evals/eval_queries.json +34 -0
- package/packs/career/skills/resume-tailor/evals/evals.json +133 -0
- package/packs/career/skills/resume-tailor/evals/files/1/posting.md +26 -0
- package/packs/career/skills/resume-tailor/evals/files/1/resume.md +32 -0
- package/packs/career/skills/resume-tailor/evals/files/2/posting.md +19 -0
- package/packs/career/skills/resume-tailor/evals/files/2/resume.md +26 -0
- package/packs/career/skills/resume-tailor/evals/files/3/posting.md +21 -0
- package/packs/career/skills/resume-tailor/evals/files/3/resume.md +32 -0
- package/packs/career/skills/resume-tailor/evals/files/4/posting.md +28 -0
- package/packs/career/skills/resume-tailor/evals/files/4/resume.md +35 -0
- package/packs/career/skills/resume-tailor/evals/files/5/posting.md +20 -0
- package/packs/career/skills/resume-tailor/evals/files/5/resume.md +32 -0
- package/packs/career/skills/resume-tailor/evals/files/6/posting.md +25 -0
- package/packs/career/skills/resume-tailor/evals/files/6/resume.md +29 -0
- package/packs/career/skills/resume-tailor/evals/files/7/posting.md +18 -0
- package/packs/career/skills/resume-tailor/evals/files/7/resume.md +23 -0
- package/packs/career/skills/resume-tailor/evals/files/8/posting.md +25 -0
- package/packs/career/skills/resume-tailor/evals/files/8/resume.typ +31 -0
- package/packs/career/skills/resume-tailor/evals/files/9/linkedin-about.txt +5 -0
- package/packs/career/skills/resume-tailor/evals/files/9/posting.md +25 -0
- package/packs/career/skills/resume-tailor/evals/files/9/resume.md +35 -0
- package/packs/career/skills/resume-tailor/manifest.json +37 -0
- package/packs/career/skills/resume-tailor/scripts/claim_check.py +250 -0
- package/packs/data-analytics/skills/metric-definition/evals/evals.json +30 -0
- package/packs/data-analytics/skills/metric-definition/evals/files/2/tables.md +42 -0
- package/packs/data-analytics/skills/metric-definition/evals/files/2/thread.md +40 -0
- package/packs/dev-team/skills/contribution-wizard/evals/evals.json +29 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/AGENTS.md +9 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/CATALOG.md +18 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/CONTRIBUTING.md +45 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/OWNERS.md +7 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/detention-claim-letter/INSTRUCTIONS.md +20 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/detention-claim-letter/skill.yaml +11 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/detention-claim-letter/tests/cases.yaml +25 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/rate-con-extractor/INSTRUCTIONS.md +20 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/rate-con-extractor/skill.yaml +8 -0
- package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/rate-con-extractor/tests/cases.yaml +32 -0
- package/packs/dev-team/skills/devils-advocate/evals/evals.json +29 -0
- package/packs/dev-team/skills/devils-advocate/evals/files/2/signups.py +116 -0
- package/packs/dev-team/skills/devils-advocate/evals/files/2/test_signups.py +69 -0
- package/packs/dev-team/skills/release-notes/evals/evals.json +29 -0
- package/packs/dev-team/skills/release-notes/evals/files/2/commits.txt +35 -0
- package/packs/research/skills/source-synthesis/evals/evals.json +30 -0
- package/packs/research/skills/source-synthesis/evals/files/2/badge-data.md +27 -0
- package/packs/research/skills/source-synthesis/evals/files/2/director-email.md +16 -0
- package/packs/research/skills/source-synthesis/evals/files/2/exit-interviews.md +17 -0
- package/packs/research/skills/source-synthesis/evals/files/2/practice-weekly-article.md +21 -0
- package/packs/research/skills/source-synthesis/evals/files/2/staff-survey-summary.md +41 -0
- package/packs/solution-architect/skills/db-schema-design/evals/evals.json +29 -0
- package/packs/solution-architect/skills/db-schema-design/evals/files/2/current_tables.sql +29 -0
- package/packs/solution-architect/skills/db-schema-design/evals/files/2/notes.md +53 -0
- package/packs/solution-architect/skills/design-doc/evals/evals.json +29 -0
- package/packs/solution-architect/skills/design-doc/evals/files/2/brief.md +18 -0
- package/packs/solution-architect/skills/design-doc/evals/files/2/eng-notes.md +30 -0
- package/packs/solution-architect/skills/tech-comparison-matrix/evals/evals.json +29 -0
- package/packs/solution-architect/skills/tech-comparison-matrix/evals/files/2/requirements.md +20 -0
- package/packs/solution-architect/skills/tech-comparison-matrix/evals/files/2/vendor-notes.md +29 -0
- package/packs/sre-oncall/skills/postmortem-generator/evals/evals.json +30 -0
- package/packs/sre-oncall/skills/postmortem-generator/evals/files/2/pager-export.txt +22 -0
- package/packs/sre-oncall/skills/postmortem-generator/evals/files/2/slack-thread.txt +28 -0
- package/packs/stakeholder-comms/skills/guide-builder/evals/evals.json +29 -0
- package/packs/stakeholder-comms/skills/guide-builder/evals/files/2/api-notes.md +44 -0
- package/packs/stakeholder-comms/skills/guide-builder/evals/files/2/sample-booking.json +19 -0
- package/profiles.json +2 -2
|
@@ -14,6 +14,35 @@
|
|
|
14
14
|
"Output ends with a 'what's solid' section so the author knows what not to touch",
|
|
15
15
|
"The review scopes to the change under review, never sweeping the whole codebase"
|
|
16
16
|
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 2,
|
|
20
|
+
"held_out": "2026-10-05",
|
|
21
|
+
"prompt": "Just finished the volunteer shift sign-up feature for the food bank's scheduling app (signups.py, tests in test_signups.py). Volunteers sign up until the shift's full, after that they go on a waitlist, and when someone cancels the first person on the waitlist gets the spot. No cancelling inside 24 hours of the shift. Tests all pass. Anything I've missed before I merge?",
|
|
22
|
+
"assertions": [
|
|
23
|
+
"It flags that sign_up compares the confirmed count with > instead of >=, so a shift with capacity N confirms N+1 volunteers",
|
|
24
|
+
"It flags that cancel picks the waitlisted row with ORDER BY created_at DESC, so the most recent person on the waitlist is promoted, not the first",
|
|
25
|
+
"It flags that the same volunteer can sign up twice for one shift because nothing checks for an existing sign-up",
|
|
26
|
+
"It flags that the 24-hour cutoff subtracts local-time datetime.now() from a start time stored in UTC, so the window is off by the server's UTC offset",
|
|
27
|
+
"It points out that test_waitlist_when_full puts three volunteers into a capacity-2 shift before asserting, so it passes because of the capacity bug",
|
|
28
|
+
"It points out that test_no_waitlist_raises passes even when ShiftFull is never raised",
|
|
29
|
+
"It does not report SQL injection, since every query is parameterised",
|
|
30
|
+
"It says plainly that the change is not ready to merge as it stands"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": 3,
|
|
35
|
+
"held_out": "2026-10-05",
|
|
36
|
+
"prompt": "wrote this quick script to dedupe our newsletter subscriber export before we import it into the new mail tool. worked fine on the 50-row sample i tried. good to run on the real file? it's about 40k rows, email is the third column\n\n```python\nimport sys\n\nrows = open(sys.argv[1]).read().split(\"\\n\")\nseen = {}\nfor r in rows[1:]:\n if not r.strip():\n continue\n cols = r.split(\",\")\n seen[cols[2]] = r\n\nout = open(sys.argv[1], \"w\")\nout.write(rows[0] + \"\\n\")\nfor r in seen.values():\n out.write(r + \"\\n\")\n```",
|
|
37
|
+
"assertions": [
|
|
38
|
+
"It answers the question near the top: not as it stands, or only on a copy of the file",
|
|
39
|
+
"It flags that emails are compared exactly, so addresses differing only in letter case or surrounding spaces are kept as separate subscribers",
|
|
40
|
+
"It flags that split(\",\") breaks on a quoted field containing a comma, which shifts the column treated as the email",
|
|
41
|
+
"It flags that the script overwrites the input file in place with no backup, and recommends writing to a new file or keeping a copy",
|
|
42
|
+
"It notes that when rows share an email the last one wins, and that whether that is the right row to keep is something to confirm",
|
|
43
|
+
"It gives a concrete fix, such as reading with the csv module and normalising the email with strip and lower",
|
|
44
|
+
"It does not state as fact what the other columns hold or what the real file contains beyond what was given"
|
|
45
|
+
]
|
|
17
46
|
}
|
|
18
47
|
]
|
|
19
48
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Volunteer shift sign-ups for the Harbourside Food Bank scheduling app."""
|
|
2
|
+
import sqlite3
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
|
|
5
|
+
CANCEL_CUTOFF_HOURS = 24
|
|
6
|
+
|
|
7
|
+
SCHEMA = """
|
|
8
|
+
CREATE TABLE IF NOT EXISTS shifts (
|
|
9
|
+
id INTEGER PRIMARY KEY,
|
|
10
|
+
starts_at TEXT NOT NULL, -- ISO 8601, stored in UTC
|
|
11
|
+
capacity INTEGER NOT NULL
|
|
12
|
+
);
|
|
13
|
+
CREATE TABLE IF NOT EXISTS signups (
|
|
14
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15
|
+
shift_id INTEGER NOT NULL REFERENCES shifts(id),
|
|
16
|
+
volunteer_id INTEGER NOT NULL,
|
|
17
|
+
status TEXT NOT NULL, -- 'confirmed' | 'waitlisted' | 'cancelled'
|
|
18
|
+
created_at TEXT NOT NULL
|
|
19
|
+
);
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ShiftFull(Exception):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TooLateToCancel(Exception):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class NotSignedUp(Exception):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def init_db(conn: sqlite3.Connection) -> None:
|
|
36
|
+
conn.executescript(SCHEMA)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _confirmed_count(conn: sqlite3.Connection, shift_id: int) -> int:
|
|
40
|
+
row = conn.execute(
|
|
41
|
+
"SELECT COUNT(*) FROM signups WHERE shift_id = ? AND status = 'confirmed'",
|
|
42
|
+
(shift_id,),
|
|
43
|
+
).fetchone()
|
|
44
|
+
return row[0]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def sign_up(conn: sqlite3.Connection, shift_id: int, volunteer_id: int,
|
|
48
|
+
allow_waitlist: bool = True) -> str:
|
|
49
|
+
"""Sign a volunteer up for a shift. Returns 'confirmed' or 'waitlisted'."""
|
|
50
|
+
shift = conn.execute(
|
|
51
|
+
"SELECT capacity FROM shifts WHERE id = ?", (shift_id,)
|
|
52
|
+
).fetchone()
|
|
53
|
+
if shift is None:
|
|
54
|
+
raise KeyError(f"no shift {shift_id}")
|
|
55
|
+
capacity = shift[0]
|
|
56
|
+
|
|
57
|
+
if _confirmed_count(conn, shift_id) > capacity:
|
|
58
|
+
if not allow_waitlist:
|
|
59
|
+
raise ShiftFull(f"shift {shift_id} is full")
|
|
60
|
+
status = "waitlisted"
|
|
61
|
+
else:
|
|
62
|
+
status = "confirmed"
|
|
63
|
+
|
|
64
|
+
conn.execute(
|
|
65
|
+
"INSERT INTO signups (shift_id, volunteer_id, status, created_at) "
|
|
66
|
+
"VALUES (?, ?, ?, ?)",
|
|
67
|
+
(shift_id, volunteer_id, status, datetime.now().isoformat()),
|
|
68
|
+
)
|
|
69
|
+
conn.commit()
|
|
70
|
+
return status
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def cancel(conn: sqlite3.Connection, shift_id: int, volunteer_id: int) -> None:
|
|
74
|
+
"""Cancel a sign-up. The first volunteer on the waitlist takes the spot."""
|
|
75
|
+
row = conn.execute(
|
|
76
|
+
"SELECT id, status FROM signups "
|
|
77
|
+
"WHERE shift_id = ? AND volunteer_id = ? AND status != 'cancelled'",
|
|
78
|
+
(shift_id, volunteer_id),
|
|
79
|
+
).fetchone()
|
|
80
|
+
if row is None:
|
|
81
|
+
raise NotSignedUp(f"volunteer {volunteer_id} is not on shift {shift_id}")
|
|
82
|
+
signup_id, status = row
|
|
83
|
+
|
|
84
|
+
starts_at = datetime.fromisoformat(
|
|
85
|
+
conn.execute(
|
|
86
|
+
"SELECT starts_at FROM shifts WHERE id = ?", (shift_id,)
|
|
87
|
+
).fetchone()[0]
|
|
88
|
+
)
|
|
89
|
+
if starts_at - datetime.now() < timedelta(hours=CANCEL_CUTOFF_HOURS):
|
|
90
|
+
raise TooLateToCancel(
|
|
91
|
+
f"cancellations close {CANCEL_CUTOFF_HOURS}h before the shift"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
conn.execute("UPDATE signups SET status = 'cancelled' WHERE id = ?", (signup_id,))
|
|
95
|
+
|
|
96
|
+
if status == "confirmed":
|
|
97
|
+
nxt = conn.execute(
|
|
98
|
+
"SELECT id FROM signups WHERE shift_id = ? AND status = 'waitlisted' "
|
|
99
|
+
"ORDER BY created_at DESC LIMIT 1",
|
|
100
|
+
(shift_id,),
|
|
101
|
+
).fetchone()
|
|
102
|
+
if nxt is not None:
|
|
103
|
+
conn.execute(
|
|
104
|
+
"UPDATE signups SET status = 'confirmed' WHERE id = ?", (nxt[0],)
|
|
105
|
+
)
|
|
106
|
+
conn.commit()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def roster(conn: sqlite3.Connection, shift_id: int) -> list[int]:
|
|
110
|
+
"""Volunteer ids confirmed for a shift, in sign-up order."""
|
|
111
|
+
rows = conn.execute(
|
|
112
|
+
"SELECT volunteer_id FROM signups "
|
|
113
|
+
"WHERE shift_id = ? AND status = 'confirmed' ORDER BY id",
|
|
114
|
+
(shift_id,),
|
|
115
|
+
).fetchall()
|
|
116
|
+
return [r[0] for r in rows]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from signups import (NotSignedUp, ShiftFull, TooLateToCancel, cancel, init_db,
|
|
7
|
+
roster, sign_up)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def conn():
|
|
12
|
+
c = sqlite3.connect(":memory:")
|
|
13
|
+
init_db(c)
|
|
14
|
+
# shift 1: next week, room for 2. shift 2: starts in two hours, room for 2.
|
|
15
|
+
c.execute(
|
|
16
|
+
"INSERT INTO shifts (id, starts_at, capacity) VALUES (1, ?, 2)",
|
|
17
|
+
((datetime.now() + timedelta(days=7)).isoformat(),),
|
|
18
|
+
)
|
|
19
|
+
c.execute(
|
|
20
|
+
"INSERT INTO shifts (id, starts_at, capacity) VALUES (2, ?, 2)",
|
|
21
|
+
((datetime.now() + timedelta(hours=2)).isoformat(),),
|
|
22
|
+
)
|
|
23
|
+
c.commit()
|
|
24
|
+
return c
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_sign_up_confirms(conn):
|
|
28
|
+
assert sign_up(conn, 1, 101) == "confirmed"
|
|
29
|
+
assert roster(conn, 1) == [101]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_unknown_shift(conn):
|
|
33
|
+
with pytest.raises(KeyError):
|
|
34
|
+
sign_up(conn, 99, 101)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_waitlist_when_full(conn):
|
|
38
|
+
sign_up(conn, 1, 101)
|
|
39
|
+
sign_up(conn, 1, 102)
|
|
40
|
+
sign_up(conn, 1, 103)
|
|
41
|
+
assert sign_up(conn, 1, 104) == "waitlisted"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_no_waitlist_raises(conn):
|
|
45
|
+
for volunteer in (101, 102, 103):
|
|
46
|
+
sign_up(conn, 1, volunteer)
|
|
47
|
+
try:
|
|
48
|
+
sign_up(conn, 1, 104, allow_waitlist=False)
|
|
49
|
+
except ShiftFull:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_cancel_promotes_waitlist(conn):
|
|
54
|
+
for volunteer in (101, 102, 103, 104):
|
|
55
|
+
sign_up(conn, 1, volunteer)
|
|
56
|
+
cancel(conn, 1, 101)
|
|
57
|
+
assert 104 in roster(conn, 1)
|
|
58
|
+
assert 101 not in roster(conn, 1)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_cancel_too_late(conn):
|
|
62
|
+
sign_up(conn, 2, 101)
|
|
63
|
+
with pytest.raises(TooLateToCancel):
|
|
64
|
+
cancel(conn, 2, 101)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_cancel_not_signed_up(conn):
|
|
68
|
+
with pytest.raises(NotSignedUp):
|
|
69
|
+
cancel(conn, 1, 555)
|
|
@@ -14,6 +14,35 @@
|
|
|
14
14
|
"No marketing adjectives; the performance line states the measured change (~3x faster export) rather than 'blazing fast'",
|
|
15
15
|
"Does not invent features or fixes not present in the log"
|
|
16
16
|
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 2,
|
|
20
|
+
"held_out": "2026-10-05",
|
|
21
|
+
"prompt": "We're cutting 3.2.0 of Tidewater Booking tomorrow. The log since 3.1.0 is in commits.txt. I need two things: the \"what's new\" text for the customer email (goes to clinic owners and to the devs who integrate with our API and webhooks), and the entry for CHANGELOG.md in the repo. Marketing wants the headline to be SMS reminders and the Google Calendar sync, and wants to say exports are 5x faster. We're a hosted service, nobody runs our code themselves.",
|
|
22
|
+
"assertions": [
|
|
23
|
+
"Google Calendar sync is not announced as shipping in 3.2.0; the output says it was reverted (commit a4e8b60 / #225) and is not in this release",
|
|
24
|
+
"The export speed-up is given as 41s to 14s for 50k bookings, or roughly 3x, and the requested '5x faster' claim is not used or is called out as unsupported by the log",
|
|
25
|
+
"The webhook field rename (start/end to starts_at/ends_at in booking payloads) is called out as a breaking change that integrators must act on, even though its commit is labelled refactor",
|
|
26
|
+
"The removal of X-Tidewater-Key header auth is called out as breaking with the instruction to switch to Bearer tokens, and breaking changes appear before the new features",
|
|
27
|
+
"The 'wip' (3b7c5e2) and 'more fixes' (71f9d3b) commits are flagged as unclear and needing someone to check, not described as specific features or fixes",
|
|
28
|
+
"The duplicate-calendar-events fix (#218) is not listed as a fix customers receive, because it was reverted along with the sync",
|
|
29
|
+
"The customer email text does not mention the twilio or vite bumps, the BookingRepo rename, CI changes or the Node 20 server requirement",
|
|
30
|
+
"Each line of the CHANGELOG entry carries a commit hash or PR number from the log"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": 3,
|
|
35
|
+
"held_out": "2026-10-05",
|
|
36
|
+
"prompt": "need release notes for pressroom (our static site CLI, the people using it are our clients' developers), going out as 0.9.0 today. this is what's gone in since 0.8.2, half from git log --oneline and half from memory, sorry:\n\nd41c0aa add --watch flag\n77e2b19 fix crash on empty frontmatter\nbump deps\n3a9f6c1 config: `output_dir` now `outDir` (old key ignored)\nc08e512 misc cleanup\n9b1d7e4 faster builds\n\nalso Priya fixed something with image paths on windows, I don't have the commit",
|
|
37
|
+
"assertions": [
|
|
38
|
+
"The output_dir to outDir config change is flagged as breaking, with the action to rename the key in existing config files because the old key is now ignored",
|
|
39
|
+
"'Faster builds' is reported without any invented figure or percentage, and the output says the size of the speed-up is not known",
|
|
40
|
+
"The Windows image-path fix is included only as far as described and marked as unconfirmed or missing its commit; no specific bug behaviour is invented for it",
|
|
41
|
+
"'misc cleanup' (c08e512) and 'bump deps' are not turned into user-facing claims",
|
|
42
|
+
"The --watch flag and the empty-frontmatter crash fix both appear as user-facing items",
|
|
43
|
+
"It does not invent dates, PR numbers, a commit count for commits it was not shown, or features not in the pasted list",
|
|
44
|
+
"It delivers usable release notes in the reply rather than only asking questions, and lists the items that need confirming before sending"
|
|
45
|
+
]
|
|
17
46
|
}
|
|
18
47
|
]
|
|
19
48
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# git log v3.1.0..release/3.2.0 --reverse --date=short --format='%h|%ad|%an|%s'
|
|
2
|
+
# repo: tidewater-booking (hosted service: API + embeddable booking widget)
|
|
3
|
+
4c1e9a2|2026-06-09|Imani Okafor|feat(reminders): send SMS reminders before a booking (#201)
|
|
4
|
+
7b03f1d|2026-06-10|Imani Okafor|test: reminder scheduler unit tests
|
|
5
|
+
1e8d4c6|2026-06-11|Imani Okafor|fix(reminders): don't send SMS for cancelled bookings (#204)
|
|
6
|
+
a93b2e0|2026-06-12|Tomasz Wrobel|chore(deps): bump twilio 5.0.4 -> 5.2.1
|
|
7
|
+
5f6a7d1|2026-06-15|Ruben Halvorsen|feat(calendar): two-way Google Calendar sync (#207)
|
|
8
|
+
c2d91b8|2026-06-16|Tomasz Wrobel|ci: cache pnpm store
|
|
9
|
+
9e4f0a3|2026-06-17|Dee Castellanos|fix(widget): date picker showed the wrong day for customers west of UTC (#209)
|
|
10
|
+
3b7c5e2|2026-06-18|Ruben Halvorsen|wip
|
|
11
|
+
d0a6e48|2026-06-19|Dee Castellanos|feat(widget): let customers reschedule from the confirmation email (#211)
|
|
12
|
+
6a2f9c7|2026-06-22|Tomasz Wrobel|refactor(webhooks): rename `start`/`end` to `starts_at`/`ends_at` in booking.* payloads (#214)
|
|
13
|
+
f18b3d5|2026-06-23|Tomasz Wrobel|docs: update webhook payload examples
|
|
14
|
+
2c9e7a4|2026-06-24|Imani Okafor|perf(export): stream CSV export; 50k bookings 41s -> 14s (#216)
|
|
15
|
+
b5d0c19|2026-06-25|Ruben Halvorsen|fix(calendar): duplicate events when a booking is edited twice (#218)
|
|
16
|
+
8e3a1f6|2026-06-26|Tomasz Wrobel|chore: rename internal BookingRepo -> BookingStore
|
|
17
|
+
0d7b4c2|2026-06-29|Imani Okafor|fix(auth): staff sessions stayed logged in after a password change (#220)
|
|
18
|
+
e6c2a95|2026-06-30|Imani Okafor|feat(reminders): per-location reminder lead time setting, default 24h (#222)
|
|
19
|
+
71f9d3b|2026-07-01|Ruben Halvorsen|more fixes
|
|
20
|
+
a4e8b60|2026-07-02|Ruben Halvorsen|Revert "feat(calendar): two-way Google Calendar sync (#207)" (#225)
|
|
21
|
+
Reverts 5f6a7d1. Google has not approved our OAuth verification yet. Re-land in 3.3.
|
|
22
|
+
3f5c8d7|2026-07-02|Ruben Halvorsen|Revert "fix(calendar): duplicate events when a booking is edited twice (#218)" (#225)
|
|
23
|
+
Reverts b5d0c19. Depends on the sync code removed in a4e8b60.
|
|
24
|
+
c9a1e04|2026-07-03|Tomasz Wrobel|chore(deps): bump vite 5.2 -> 5.3
|
|
25
|
+
5b2d7f9|2026-07-06|Dee Castellanos|fix(widget): "Book" button unresponsive on iOS Safari 16 (#227)
|
|
26
|
+
1a6e3c8|2026-07-07|Tomasz Wrobel|feat(api): GET /v2/bookings accepts a `location_id` filter (#229)
|
|
27
|
+
d8f4b21|2026-07-08|Tomasz Wrobel|chore(node): require Node 20 on our servers; drop Node 18 from CI (#230)
|
|
28
|
+
9c0e5a7|2026-07-09|Imani Okafor|fix(reminders): reminders went out an hour early after the clocks changed (#232)
|
|
29
|
+
4e7a2d0|2026-07-10|Dee Castellanos|test: e2e for reschedule flow
|
|
30
|
+
b1c6f83|2026-07-13|Imani Okafor|fix(export): CSV export dropped the notes column (#234)
|
|
31
|
+
6d9b0e5|2026-07-14|Tomasz Wrobel|feat(api)!: remove deprecated `X-Tidewater-Key` header auth; use Bearer tokens (#236)
|
|
32
|
+
2f8c4a1|2026-07-15|Dee Castellanos|docs: changelog housekeeping
|
|
33
|
+
e0b7d36|2026-07-16|Dee Castellanos|style: prettier
|
|
34
|
+
7a3e9c2|2026-07-17|Dee Castellanos|fix: typo in reschedule email subject
|
|
35
|
+
0c5d8b4|2026-07-20|Tomasz Wrobel|ci: bump runner image
|
|
@@ -14,6 +14,36 @@
|
|
|
14
14
|
"Does not add facts, benchmarks or figures that are not in the three sources, unless tagged [outside]",
|
|
15
15
|
"Runs or recommends the citation checker and reports its result"
|
|
16
16
|
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 2,
|
|
20
|
+
"held_out": "2026-10-05",
|
|
21
|
+
"prompt": "We're a 45-person architecture practice and the directors are split on the 3-days-in-the-studio rule. Everything we have is in this folder: staff-survey-summary.md, badge-data.md, exit-interviews.md, director-email.md and practice-weekly-article.md. Can you pull it together for Thursday's directors' meeting? The choice is keep 3 days, drop to 2, or go up to 4 like Lucian wants. And tell me what changing it would do to retention.",
|
|
22
|
+
"assertions": [
|
|
23
|
+
"It corrects the claim that productivity is down 20%: the Castellan survey found 20% of managers believed productivity had fallen (and 34% believed it had risen), and it measured perceptions, not output",
|
|
24
|
+
"It treats Lucian's email and the Practice Weekly article as resting on the same Castellan survey, not as two independent pieces of evidence",
|
|
25
|
+
"It notes the Castellan fieldwork dates from October–November 2021 and treats it as dated or weak evidence for a 2026 decision",
|
|
26
|
+
"It points out that the Q1 answers in the staff survey add up to 40 (27 + 9 + 4) against 38 stated responses",
|
|
27
|
+
"It states that actual attendance already averages 2.4 days a week, below the 3-day rule, with 14 of 45 staff averaging 3 or more days",
|
|
28
|
+
"On retention it says the material cannot answer the question: three leavers, only one of whom (the technologist who moved to Bristol) linked leaving to office days; it gives no predicted retention effect",
|
|
29
|
+
"Each finding names the file it comes from, and the findings are grouped by topic, not written as one summary per file",
|
|
30
|
+
"It does not bring in studies, statistics or claims about hybrid working that are not in the five files"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": 3,
|
|
35
|
+
"held_out": "2026-10-05",
|
|
36
|
+
"prompt": "I talked to five cafe owners last week about whether they'd pay for our stock-ordering app. My notes are rubbish, sorry, here's what I've got:\n\n- Marta, Kiln St Coffee (2 sites). Orders by WhatsApp to 3 suppliers. Forgets oat milk 'every other week'. Said 'I'd try it'. Asked if it works with her till (Lumo), I didn't know.\n- Joss, The Larder. Does the ordering Sunday nights, takes about 2 hrs. 'If it saved me an hour I'd pay, maybe £30 a month?'\n- Dee, Dee's. Happy with her spreadsheet, 'not a problem for me'. But said her friend who runs a bakery would def pay £50/mo.\n- Priyank, Corner Cup. Ran out of time, he mostly talked about a supplier delivering late. Didn't get to the app.\n- can't find my notes for the fifth one. think it was positive\n\nCan you pull together what we learned? Do they want it, and what should we charge?",
|
|
37
|
+
"assertions": [
|
|
38
|
+
"It delivers the write-up from the notes as given and does not stop to ask for better notes first",
|
|
39
|
+
"It says only one owner, Joss, named a price they would pay (about £30 a month, said tentatively and conditional on saving an hour)",
|
|
40
|
+
"It does not treat the £50 a month figure as evidence of willingness to pay: it is Dee's second-hand remark about a bakery owner who was not interviewed",
|
|
41
|
+
"It says Dee does not see ordering as a problem and that Priyank's conversation never reached the app, so at most two of the four noted conversations show interest",
|
|
42
|
+
"The fifth conversation is treated as no evidence because the notes are missing; 'think it was positive' is not counted as a positive result",
|
|
43
|
+
"Marta's 'I'd try it' is reported as interest in trying, not as agreement to pay",
|
|
44
|
+
"It says five conversations are too few to set a price or to conclude that cafe owners want it, and gives any price suggestion as tentative",
|
|
45
|
+
"It does not add market figures, competitor prices or cafe-industry facts that are not in the notes"
|
|
46
|
+
]
|
|
17
47
|
}
|
|
18
48
|
]
|
|
19
49
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Studio door badge data
|
|
2
|
+
|
|
3
|
+
Pulled by Odette Marsh from the access system. Covers 8 weeks, Monday 5 January to
|
|
4
|
+
Friday 27 February 2026. 45 staff.
|
|
5
|
+
|
|
6
|
+
## Average attendance
|
|
7
|
+
|
|
8
|
+
Average days in the studio per person per week: **2.4**
|
|
9
|
+
|
|
10
|
+
Staff who averaged 3 or more days a week over the period: **14 of 45**
|
|
11
|
+
|
|
12
|
+
## By weekday (share of staff badged in, average over the 8 weeks)
|
|
13
|
+
|
|
14
|
+
| Day | Share in |
|
|
15
|
+
|---|---|
|
|
16
|
+
| Monday | 31% |
|
|
17
|
+
| Tuesday | 74% |
|
|
18
|
+
| Wednesday | 77% |
|
|
19
|
+
| Thursday | 40% |
|
|
20
|
+
| Friday | 18% |
|
|
21
|
+
|
|
22
|
+
## Caveats
|
|
23
|
+
|
|
24
|
+
- The reader misses anyone let in by a colleague. Facilities think the undercount is small
|
|
25
|
+
but nobody has measured it.
|
|
26
|
+
- The period includes no school holidays.
|
|
27
|
+
- Site visits and client offices are not recorded, so a day on site looks like a day at home.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
From: Lucian Farrow
|
|
2
|
+
To: Directors
|
|
3
|
+
Date: 2 March 2026
|
|
4
|
+
Subject: Office days, my view before the meeting
|
|
5
|
+
|
|
6
|
+
All,
|
|
7
|
+
|
|
8
|
+
I'll be blunt. Productivity is down 20% since hybrid. It's in the Castellan research, see
|
|
9
|
+
the Practice Weekly piece I forwarded. That is a fifth of our output gone.
|
|
10
|
+
|
|
11
|
+
I also hear from two clients that they like seeing the team together when they visit.
|
|
12
|
+
|
|
13
|
+
Three days is not working. I want us at four days from the summer, with Friday as the
|
|
14
|
+
flexible day.
|
|
15
|
+
|
|
16
|
+
Lucian
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Exit interview notes, September 2025 to February 2026
|
|
2
|
+
|
|
3
|
+
Notes taken by Odette Marsh. Three people left in the period.
|
|
4
|
+
|
|
5
|
+
## Leaver A, Part 2 assistant, left October 2025
|
|
6
|
+
|
|
7
|
+
Went to a larger practice for a higher salary. Asked about office days: "fine, didn't
|
|
8
|
+
bother me". Main reason given: pay.
|
|
9
|
+
|
|
10
|
+
## Leaver B, senior architectural technologist, left January 2026
|
|
11
|
+
|
|
12
|
+
Moved to Bristol for family reasons. Said: "Three days in made it impossible to stay on
|
|
13
|
+
after the move. Two I could have done." Main reason given: relocation.
|
|
14
|
+
|
|
15
|
+
## Leaver C, associate, left February 2026
|
|
16
|
+
|
|
17
|
+
Left to set up their own practice. Office days were not discussed.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Is hybrid hurting studios?
|
|
2
|
+
|
|
3
|
+
Practice Weekly, 9 February 2026. By Ines Calloway.
|
|
4
|
+
|
|
5
|
+
Five years on from the first lockdowns, studio principals are still arguing about how many
|
|
6
|
+
days their teams should be at their desks.
|
|
7
|
+
|
|
8
|
+
The most widely quoted figures come from the Castellan Institute's Workplace Pulse survey.
|
|
9
|
+
Castellan questioned 1,200 managers at professional-services firms. Fieldwork ran in October
|
|
10
|
+
and November 2021. Asked how their team's productivity had changed since moving to hybrid
|
|
11
|
+
working, 20% of managers said they believed it had fallen, 46% said it had not changed and
|
|
12
|
+
34% said they believed it had risen. The survey recorded managers' perceptions. It did not
|
|
13
|
+
measure output.
|
|
14
|
+
|
|
15
|
+
Castellan did not publish a breakdown for architecture practices.
|
|
16
|
+
|
|
17
|
+
"You cannot teach someone to detail a junction over a video call," said one studio principal
|
|
18
|
+
who asked not to be named. Another said her practice had dropped its fixed days altogether
|
|
19
|
+
and "nobody noticed, except the people who used to commute two hours".
|
|
20
|
+
|
|
21
|
+
No newer wave of the survey has been published.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Staff survey on office days: summary
|
|
2
|
+
|
|
3
|
+
Compiled by Odette Marsh, studio manager, 12 March 2026.
|
|
4
|
+
Sent to all 45 staff. 38 responses. Anonymous.
|
|
5
|
+
|
|
6
|
+
## Q1. How many days a week would you choose to be in the studio?
|
|
7
|
+
|
|
8
|
+
| Answer | Responses |
|
|
9
|
+
|---|---|
|
|
10
|
+
| 2 or fewer | 27 |
|
|
11
|
+
| 3 | 9 |
|
|
12
|
+
| 4 or 5 | 4 |
|
|
13
|
+
|
|
14
|
+
## Q2. "The 3-day rule helps me do my job well."
|
|
15
|
+
|
|
16
|
+
| Answer | Responses |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Agree | 11 |
|
|
19
|
+
| Neutral | 9 |
|
|
20
|
+
| Disagree | 18 |
|
|
21
|
+
|
|
22
|
+
## Q3. Main reason you come in (pick one)
|
|
23
|
+
|
|
24
|
+
| Answer | Responses |
|
|
25
|
+
|---|---|
|
|
26
|
+
| Design reviews and crits | 19 |
|
|
27
|
+
| Mentoring / being mentored | 8 |
|
|
28
|
+
| Client meetings | 7 |
|
|
29
|
+
| Home is not a good place to work | 4 |
|
|
30
|
+
|
|
31
|
+
## Free text
|
|
32
|
+
|
|
33
|
+
- 7 respondents identified themselves as Part 1 or Part 2 assistants. 6 of those 7 wrote
|
|
34
|
+
that they learn more on days when senior staff are in.
|
|
35
|
+
- "Tuesdays are great. Fridays I sit on my own with my headphones on."
|
|
36
|
+
- "I'd come in more if I knew my team would be there."
|
|
37
|
+
- "Three days is fine, it's being told which three I mind."
|
|
38
|
+
|
|
39
|
+
## Limits
|
|
40
|
+
|
|
41
|
+
No breakdown by team or seniority beyond the assistants who identified themselves.
|
|
@@ -11,6 +11,35 @@
|
|
|
11
11
|
"Every index maps to a real predicate from the listed queries rather than being speculative",
|
|
12
12
|
"Denormalization, where proposed, names the read it serves and the write cost it adds"
|
|
13
13
|
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": 2,
|
|
17
|
+
"held_out": "2026-10-05",
|
|
18
|
+
"prompt": "We're rebuilding the booking system for our vet clinics and I need the database tables designed properly this time. My notes on what it has to do are in notes.md and the tables we have today are in current_tables.sql. Can you give me the new schema, with the SQL to create it and the queries for the screens and the report?",
|
|
19
|
+
"assertions": [
|
|
20
|
+
"It includes CREATE TABLE statements for the new schema, with owners and pets as their own tables instead of the owner_name, owner_phone and pet_name columns repeated on each appointment",
|
|
21
|
+
"Double-booking a vet is prevented by a database constraint that handles appointments of different lengths (for example an exclusion constraint on a time range, or one row per time slot with a unique key), not only by an application check or by a unique key on vet and start time alone",
|
|
22
|
+
"Cancelled appointments are excluded from that constraint, so a cancelled time can be booked again",
|
|
23
|
+
"It points out that the notes say Postgres 15 while the contractor says the current app runs on MySQL 8, and states which one the SQL is written for",
|
|
24
|
+
"Prices are stored as an exact type (integer cents or a decimal/numeric), not FLOAT, and the price is modelled per appointment type per clinic",
|
|
25
|
+
"Appointment times are stored with a time zone or in UTC with each clinic's time zone recorded, and it notes that storing Sydney local time is wrong for the Brisbane clinics",
|
|
26
|
+
"It says the no-show report cannot be produced from the current statuses (booked, cancelled, completed) and adds a no-show status or an equivalent field",
|
|
27
|
+
"It gives a query or a named index for the vet's-day screen that leads on the vet and the appointment start time, and does not add tables for invoicing, payments, stock or clinical records"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": 3,
|
|
32
|
+
"held_out": "2026-10-05",
|
|
33
|
+
"prompt": "need somewhere to store sensor readings from our greenhouses. about 200 sensors, each does temperature + humidity, a reading every minute or so I think. I want to chart the last 24h for any one sensor and get an alert when something goes out of range. haven't picked a database yet. what tables do I need?",
|
|
34
|
+
"assertions": [
|
|
35
|
+
"It gives concrete table definitions (SQL or an equivalent column-by-column layout) in the reply, and does not stop to ask questions before delivering them",
|
|
36
|
+
"The readings table has a key or index on the sensor and the reading time together, and the reply ties it to the last-24-hours chart for one sensor",
|
|
37
|
+
"It works out the volume from the numbers given: about 288,000 readings a day (roughly 105 million a year) at one reading per sensor per minute, or an equivalent calculation",
|
|
38
|
+
"It names a database to use, or says which one it assumed, with a reason linked to the chart and alert queries",
|
|
39
|
+
"It says how long readings should be kept is not known and lists it as something to decide, without stating a retention period as if it had been given",
|
|
40
|
+
"It stores the allowed range somewhere (per sensor, per greenhouse or per sensor type) and says the actual range values were not given; any numbers shown are labelled as examples",
|
|
41
|
+
"Reading times are stored with a time zone or in UTC"
|
|
42
|
+
]
|
|
14
43
|
}
|
|
15
44
|
]
|
|
16
45
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Dump of the tables the current booking app uses. Schema only.
|
|
2
|
+
-- NOTE (Devraj): all times are stored as Sydney local time, for every clinic.
|
|
3
|
+
|
|
4
|
+
CREATE TABLE clinics (
|
|
5
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
6
|
+
name VARCHAR(80) NOT NULL,
|
|
7
|
+
city VARCHAR(40) NOT NULL
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
CREATE TABLE vets (
|
|
11
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
12
|
+
full_name VARCHAR(120) NOT NULL,
|
|
13
|
+
clinic_id INT NOT NULL -- home clinic only
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE TABLE appointments (
|
|
17
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
18
|
+
clinic_id INT NOT NULL,
|
|
19
|
+
vet_id INT NOT NULL,
|
|
20
|
+
appt_time DATETIME NOT NULL, -- start time, Sydney local
|
|
21
|
+
appt_type VARCHAR(40) NOT NULL, -- free text: 'consult', 'Consult', 'vacc', ...
|
|
22
|
+
price FLOAT,
|
|
23
|
+
owner_name VARCHAR(120),
|
|
24
|
+
owner_phone VARCHAR(40),
|
|
25
|
+
pet_name VARCHAR(80),
|
|
26
|
+
status VARCHAR(20) NOT NULL -- 'booked', 'cancelled', 'completed'
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
-- no indexes other than the primary keys
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Pawsley Vets: booking rebuild, working notes
|
|
2
|
+
|
|
3
|
+
Written by Imogen Tarrant (practice systems lead), last touched 22 Sept 2026.
|
|
4
|
+
|
|
5
|
+
## Who we are
|
|
6
|
+
|
|
7
|
+
- 6 clinics. Four around Sydney (Marrickville, Newtown, Manly, Parramatta) and two in
|
|
8
|
+
Brisbane (Paddington, West End).
|
|
9
|
+
- About 38 vets. Seven of them work at two clinics on different days of the week.
|
|
10
|
+
- Roughly 450 appointments a day across the group.
|
|
11
|
+
- About 21,000 active pets belonging to about 15,000 owners. An owner can have several pets.
|
|
12
|
+
|
|
13
|
+
## Appointment types
|
|
14
|
+
|
|
15
|
+
| Type | Length |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Consult | 15 min |
|
|
18
|
+
| Vaccination | 15 min |
|
|
19
|
+
| Extended consult | 30 min |
|
|
20
|
+
| Surgery consult | 60 min |
|
|
21
|
+
|
|
22
|
+
Lengths will probably change (we are talking about 20-minute consults). Each type has a price,
|
|
23
|
+
and the price is different at each clinic.
|
|
24
|
+
|
|
25
|
+
## What the new system has to do
|
|
26
|
+
|
|
27
|
+
1. **A vet's day.** Reception pulls up every appointment for one vet on one date, in time
|
|
28
|
+
order. This is the screen that is open all day at every front desk and refreshes every few
|
|
29
|
+
seconds. It has to feel instant.
|
|
30
|
+
2. **A clinic's day.** Same thing but for every vet working at one clinic that day.
|
|
31
|
+
3. **Owner portal.** An owner logs in and sees the upcoming appointments for all of their pets.
|
|
32
|
+
4. **Free slots.** Given a vet (or a clinic) and a date, what times are still open.
|
|
33
|
+
5. **Monthly report.** No-show rate per clinic per month. The directors ask for this every
|
|
34
|
+
month and today somebody counts it by hand from the paper diary.
|
|
35
|
+
6. **No double-booking a vet. Ever.** It happened 11 times last quarter. The current app checks
|
|
36
|
+
whether the slot is free and then inserts, and two receptionists at different desks can both
|
|
37
|
+
get through. If an appointment is cancelled, that time has to become bookable again.
|
|
38
|
+
|
|
39
|
+
## Stack
|
|
40
|
+
|
|
41
|
+
Database: Postgres 15 on the managed service (that is what the hosting invoice says).
|
|
42
|
+
|
|
43
|
+
Pasted from Devraj (contractor who maintains the current app), 18 Sept:
|
|
44
|
+
|
|
45
|
+
> the current booking app is on MySQL 8, has been since 2021. I'd just keep it there tbh,
|
|
46
|
+
> no reason to move
|
|
47
|
+
|
|
48
|
+
I have not chased up which of those is right.
|
|
49
|
+
|
|
50
|
+
## Other
|
|
51
|
+
|
|
52
|
+
- The existing tables are in current_tables.sql. I know they are not great.
|
|
53
|
+
- Not in scope for this piece of work: invoicing and payments, stock, clinical records.
|
|
@@ -12,6 +12,35 @@
|
|
|
12
12
|
"The rollout section names stages and what would halt it",
|
|
13
13
|
"Facts not supplied by the user are marked as open questions rather than invented"
|
|
14
14
|
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": 2,
|
|
18
|
+
"held_out": "2026-10-05",
|
|
19
|
+
"prompt": "Can you write the design doc for offline mode in the inspector app? Product's brief is brief.md and Halvard's notes from our eng discussion are eng-notes.md. It goes to design review on Thursday so it needs to stand on its own. Please include how much storage it needs per device too.",
|
|
20
|
+
"assertions": [
|
|
21
|
+
"It flags that the timeline does not work as written: 14 weeks from a 14 Dec 2026 start ends around 22 March 2027, after the 1 March 2027 Calder County deadline, and it proposes a way through or raises it as a decision",
|
|
22
|
+
"The size of the problem is given as 1,120 of 8,400 inspections (about 13%) with sync or save errors, or the brief's 'around 40%' is called out as unsupported by the logged numbers",
|
|
23
|
+
"It does not apply plain last-write-wins to signed inspections; it surfaces the conflict between the brief and the compliance rule that a signed inspection must never be overwritten",
|
|
24
|
+
"The server-side sync endpoint is shown as depending on the Platform team with no owner confirmed and nobody yet asked",
|
|
25
|
+
"Per-device storage need is stated as not yet measured, with no invented figure, and measuring it is listed as a to-do or open question",
|
|
26
|
+
"At least two of the alternatives from the notes (outbox queue, CRDT library, PDF re-keying) are described with the reason each was not chosen as the main approach",
|
|
27
|
+
"The rollout starts with the 12 Calder County volunteers and gives at least one concrete, checkable condition for widening or stopping it",
|
|
28
|
+
"It does not invent crash rates, backend load figures, costs or device counts that are not in the two files"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": 3,
|
|
33
|
+
"held_out": "2026-10-05",
|
|
34
|
+
"prompt": "need a one-pager design doc by end of day for moving our cron jobs off the old box. there's like 30ish of them on a single ec2 instance (cron-01), some of them nobody knows what they do anymore. thinking either k8s cronjobs since we already have a cluster, or some managed scheduler. Jonas wants it done before the OS on that instance goes end of life but I don't remember the date. 4 of us on the team.",
|
|
35
|
+
"assertions": [
|
|
36
|
+
"It delivers a design doc in the reply rather than only asking questions",
|
|
37
|
+
"The OS end-of-life date is recorded as unknown and to be confirmed; no specific date is invented",
|
|
38
|
+
"The jobs nobody understands are dealt with explicitly, through an inventory or audit step or a named risk with a mitigation, not assumed to be understood",
|
|
39
|
+
"Both Kubernetes CronJobs and a managed scheduler are described, with a reason for preferring one or a statement of what would decide it",
|
|
40
|
+
"The job count stays approximate ('about 30'); it does not invent an exact count, job names, schedules or failure rates",
|
|
41
|
+
"It says how a migrated job is checked before its old cron entry is removed, and how to go back if it misbehaves",
|
|
42
|
+
"It is about one page: roughly 700 words or fewer"
|
|
43
|
+
]
|
|
15
44
|
}
|
|
16
45
|
]
|
|
17
46
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Brief: offline mode for the Stonecrop inspector app
|
|
2
|
+
|
|
3
|
+
From: Marguerite Adeyemi (Product), 28 Sept 2026
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
Inspectors lose work when they're in basements, plant rooms and rural sites with no signal. It's our number one support complaint. Around 40% of inspections are done with no signal, so this affects nearly half of what we do.
|
|
7
|
+
|
|
8
|
+
## What I want
|
|
9
|
+
- An inspector can open, fill in, photograph and sign an inspection with no connection, and it syncs by itself when they're back online.
|
|
10
|
+
- Works for at least 72 hours offline (covers a long weekend on a remote site).
|
|
11
|
+
- If two people edited the same inspection, last write wins. Keep it simple.
|
|
12
|
+
- Android tablets first. iOS can follow.
|
|
13
|
+
|
|
14
|
+
## Deadline
|
|
15
|
+
Calder County's contract renewal is 1 March 2027 and they've said offline is a condition. It has to be live for them before that date.
|
|
16
|
+
|
|
17
|
+
## Team
|
|
18
|
+
Two mobile engineers (Ines and Kwabena) once they're free.
|