Skip to content

Import people onto the lifecycle sheet through gates (#65) - #69

Merged
rachmo merged 2 commits into
mainfrom
claude/hawk-mod-issue-65-295f38
Oct 15, 2026
Merged

rachmo merged 2 commits into
mainfrom
claude/hawk-mod-issue-65-295f38

Conversation

@rachmo

@rachmo rachmo commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Closes #65 once it has been used for one real import (see "Not done yet").

What this adds

A Claude skill, .claude/skills/import-people/. It's the procedure for putting people onto the lifecycle sheet from intake forms, CSVs, exports or lists. It loads by itself whenever someone asks for that. It wraps lifecycle-sheet.md §4 in four gates, each ending on a pass-or-fail check:

  1. Before touching the sheet: a named version in Version history, a /hawkmod lifecycle plan baseline, and the headers checked against SHEET_TABS. The operator confirms the highest Person ID, which tells us whether the Drive read came back complete. They also give the live row 1 and the first empty row of each tab.
  2. Map, match and approve: at most 25 people per batch. Each person is matched as existing, new (next Person ID) or set aside. The table shows dates to get, values to add to Reference_Lists, expected sheet problems and derived values. Nothing is written until the operator approves it in chat.
  3. The operator pastes, one block per tab for the whole batch, at a cell typed into the Name Box, with Cmd+Shift+V. New people go in as Inactive, which the roster, group and onboarding runs all skip. Rows added for people already on the sheet go in a separate pass, timed against the :20 run, because they can't be staged as Inactive.
  4. Verify, then go live: the agent re-reads every new row by column name, never by position. The dry runs are checked once while the new people are Inactive and again after they're flipped to Active.

Backfilling existing people. Some people were first entered with only enough to get them into Slack, so the skill also covers filling in their missing details. It names three kinds of write:

  • add: a new person;
  • append: a new row for someone already on the sheet, such as a guardian;
  • fill: a blank cell on an existing row.

An append or fill is live when its column is in SHEET_TABS, because hawk-mod reads it, and quiet otherwise. The passes go in order: adds (staged as Inactive), then quiet, then live. The live pass is pasted just after a :20 run and checked before the next one. A fill only ever writes into blank cells, one span at a time. Before each span, the operator confirms that column A of the row shows the expected Person ID. Afterwards, every other cell in the row must equal what was read in gate 1.

mapping.md records the five intake formats seen so far, structure only. It came from the first manual conversion and was corrected where that conversion broke a sheet rule. Most importantly, a "yes" or TRUE now becomes a blank cell plus a "date to get", never an invented expiry date. A Slack export's member ID is not typed in either, because slack-ids apply fills it.

A duplicate check in parseSheet. Two Person IDs that share a School Email or RHR Email (compared by addressKey) are listed as a sheet problem, by Person ID and row, never by the address. It's the likeliest import mistake, and it backs up a Drive read that comes back incomplete. Personal Email isn't compared, because siblings can share a family address. Once this deploys, any duplicates already on the sheet will show up in /hawkmod lifecycle plan. That only changes what the plan reports.

docs/lifecycle-sheet.md §2 documents the check, and §4 points to the skill.

Decisions (on #65, 2026-10-15)

  • The operator pastes, and the agent never writes to the sheet. Having the agent edit the sheet in Chrome was considered and rejected.
  • The agent reads the whole sheet through the Drive connector. Because of that, personal data stays in chat, and nothing read from the sheet goes into files, commits, issues or PRs.
  • A student with no School Email is imported with that cell blank. That differs from Safe procedure for importing people into the lifecycle sheet (Google Form or other data) #65's first draft, which set them aside. The sheet problem it raises is listed as expected, and it prompts a manual follow-up.
  • At most 25 people per batch.

Not done yet

#65 is done once the skill has been used for one real import, with the dry runs checked against the plan, and anything it got wrong fixed in the procedure.

Verification

npm run typecheck && npm test && npm run format:check pass (855 tests, 5 of them new, for the duplicate check).

🤖 Generated with Claude Code

Rachel Moore and others added 2 commits October 15, 2026 15:30
A repo skill, .claude/skills/import-people, wraps lifecycle-sheet.md §4
in four gates: a named version and a dry-run baseline; a table approved
in chat before anything is written; the operator pastes one block per
tab, with new people Inactive; then hawk-mod's dry runs are checked at
Inactive and again at Active. The agent reads the sheet through Drive
and never writes to it. mapping.md records the five known intake
formats, structure only, corrected where the first manual conversion
broke a sheet rule (a "yes" became an invented expiry date).

parseSheet now lists two Person IDs sharing a School or RHR Email as a
sheet problem, by Person ID and row, never by value: the likeliest
import mistake, and the backstop for a Drive read that comes back
incomplete. Personal Email is not compared, since siblings can share a
family address.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Some people were first entered with only enough to get them into Slack,
and their missing details now need filling in. The skill assumed every
import added people, and forbade editing an existing cell, so it could
only set a backfill aside.

It now names three writes: add (a new person), append (a new row for an
existing one) and fill (a blank cell on an existing row). An append or
fill is live when its column is in SHEET_TABS and quiet otherwise.
Passes go adds (staged Inactive), then quiet, then live (just after :20,
checked before the next run). A fill writes only blank cells, one span
at a time, after the operator confirms the row's Person ID in column A.
Afterwards every other cell in the row must equal the gate 1 read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@rachmo
rachmo requested review from tytremblay and willtoth October 15, 2026 19:49
@rachmo
rachmo merged commit b818144 into main Oct 15, 2026
1 check passed
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.

Safe procedure for importing people into the lifecycle sheet (Google Form or other data)

2 participants