Skip to content

Claude/tooling/allowlist script - #2

Open
jeffoberlander wants to merge 50 commits into
burrage:mainfrom
curiouslearning:claude/tooling/allowlist-script
Open

Claude/tooling/allowlist script#2
jeffoberlander wants to merge 50 commits into
burrage:mainfrom
curiouslearning:claude/tooling/allowlist-script

Conversation

@jeffoberlander

Copy link
Copy Markdown

Added a script to allow someone with the right GCP permissions to modify the Google Secret list of authorized users

dz4va and others added 30 commits April 1, 2026 20:07
Migrate to Cloud Run with GCP Secret Manager and Cloud Storage
Add Google OAuth2 authentication and CI/CD pipeline
Tasks that would round down to 0 days should be counted as 0.5 days minimum because some effort was expended!
Fixed 0.5 day rounding on 0 day round down tasks
- Add /history page showing completed retrospectives grouped by quarter
- Add /api/history?project=<key> endpoint returning quarter-grouped data
- Add History button next to Analytics button on the main page
- Add History link to the Analytics page nav
- Store epicSummary when saving a retrospective (getEpicAndIssues already
  runs in the /run handler so no extra Jira call is needed)
- Add getRetrospectivesByProject() to storage layer for project-scoped queries
- Add src/domain/history.ts with groupByQuarter() pure helper
- Add 7 unit tests covering groupByQuarter() edge cases
- Wire up vitest as the test runner
- /api/history now accepts project=all (or no param) to return all projects
- Each entry now includes jiraUrl and projectName in the API response
- History table shows epic title as the linked text (not the key) pointing
  to the correct Jira URL
- Removed redundant separate Epic key + Retrospective Name columns; single
  Epic column now carries the full title linked to Jira
- Cross-project view adds a Project column showing the team/workspace name
- Project selector defaults to All Projects with individual projects below
- Empty state updated: tells users retros appear after generation on the
  home page, with a direct link back
- Back link from All Projects view goes to / rather than /?project=all
getFullYear/getMonth are local-time and can misassign generatedAt values
near quarter boundaries when the server timezone differs from UTC.
Switch all date reads in getQuarterLabel and groupByQuarter to the UTC
equivalents (getUTCFullYear, getUTCMonth) so quarter assignment is
consistent with the ISO timestamps stored in retrospectives.json.
updateNavLinks() was inside the projects.length > 0 guard, leaving the
History and Analytics hrefs stuck at '#' when the project list is empty.
Hoist updateNavLinks() out of the guard so the links always resolve;
loadEpics() remains gated on projects being available.
…rospective-page

BTT-551 Add a "History" Retrospective page per project space
claude and others added 20 commits July 9, 2026 14:51
…development-permissions-sesn_01Vgf6729N66QXr6HpKJZprL

BTT-554 Add productdevelopment@curiouslearning.org editor permissions to retrospective documents
In both the project-specific and All Projects History views, split
the existing Epic column into:
- Epic: shows the epic key (e.g. BTT-123) as a Jira link
- Summary: new column immediately to the right showing the epic's
  summary text (epicSummary)
…-column-to-history

BTT-555 Add "Summary" column to History table
- Add overwriteDoc() to google.ts that clears and rewrites an existing
  Google Doc without creating a new one, moving it, or re-sharing it.
  Formatting passes (styles, bold labels, bullets, Jira hyperlinks) are
  shared with createDoc via the new private applyDocFormatting helper.
- Update buildRetrospective() to accept an optional existingDocumentUrl.
  When provided the doc ID is extracted from the URL and overwriteDoc is
  called; the same URL is returned so the History entry stays unchanged.
- Update POST /run to accept existing_document_url in the request body
  and forward it to buildRetrospective.
- On the main page, epics that already have a retrospective now show a
  'Regenerate Retrospective' button alongside the existing doc link.
  Clicking it calls the /run endpoint with the existing document URL,
  overwrites the doc in-place, and updates the displayed timestamp.
- Add tests for overwriteDoc covering content deletion, insertion,
  skipping empty-doc deletion, no documents.create call, and no
  re-sharing via permissions.create.
…n /run

Before passing existing_document_url to buildRetrospective, load the
stored retrospective for the given epic_key and reject with HTTP 400 if
the supplied URL does not match. This prevents the destructive overwrite
path from being triggered against arbitrary document URLs that were not
created by this application for the requested epic.
…retrospective

BTT-553 Allow retrospective to be re-generated from the main page
- Add addWorkingDays and advanceToMonday helpers to workingDays.ts
- Add anticipatedCompletion.ts domain module with computeAnticipatedCompletionDate and formatDateMMDDYYYY
- Add getOpenTicketCount to jira.ts (queries To Do / In Progress / Review/QA statuses)
- Add computeProjectAverageCycleTime to analytics.ts (last 90 days of completed issues)
- Update /api/epics endpoint to include anticipatedCompletionDate per epic
- Update main page UI to display 'Anticipated completion date:' below the tasks completed line
- Add 12 tests covering the new domain logic
…ate display

- calculateCycleTimesForIssues: replace serial for-loop with bounded
  concurrency (5 parallel changelog fetches) to unblock /api/epics
- computeProjectAverageCycleTime: add 5-minute TTL in-process cache keyed
  by projectKey+daysBack so repeated /api/epics calls skip the fetch entirely
- progressHtml zero-task branch: append anticipatedDateHtml so epics with no
  tracked tasks still render the anticipated completion date when available
…d-completion-date

BTT-560 Add "Anticipated completion date" to open epic cards
computeProjectAverageCycleTime can fail (Jira API error, project with no
completed-issue history). Previously it shared a Promise.all with
getOngoingEpics, so any failure killed the entire response and the browser
received {error:...} instead of an array, causing 'epics.map is not a
function'. Now the call falls back to 0 on error, epics always load, and
the anticipated date is simply omitted when cycle-time data is unavailable.
…d-completion-date

BTT-560 Fix "epics.map is not a function" error on main page load
Two bugs in getOpenTicketCount caused every /api/epics request to 500:

1. fields: [] is rejected by the Jira Cloud search API; changed to
   fields: ["status"] to match the working pattern in getEpicProgress.
   Switched from result.total (unreliable with maxResults:0) to
   result.issues.length for consistency.

2. Any Jira error from getOpenTicketCount propagated uncaught through the
   per-epic Promise.all and killed the entire response. Added .catch(() => 0)
   at the call site so a failure omits the anticipated date for that epic
   without taking down the rest of the page.
…d-completion-date

BTT-560 Fix getOpenTicketCount crashing /api/epics with 500
The redeploy command in README pointed at an image tag that does not
exist -- cloudbuild.yaml only ever tags $COMMIT_SHA, so following the
doc verbatim failed with "Image not found". Look up the image the
service is currently running instead.

Also document that a new Secret Manager version replaces the entire
payload rather than appending, since retrospective-allowed-emails holds
a comma-separated list and the old example invited overwriting it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Managing app access was a four-step gcloud procedure documented in prose:
read the secret, append to the comma-separated list, write the whole value
back, then redeploy the running image to force it live. Every step had a way
to go wrong that silently revokes access -- writing just the new address
drops everyone else, echo leaves a newline inside the last address, and the
image reference has to be looked up because cloudbuild.yaml only ever tags
$COMMIT_SHA.

Put those commands in one place instead. scripts/allowlist.sh does the
read-modify-write with dedupe, a --dry-run preview, verification that the new
version matches what was written, and guards against emptying the list or
removing your own account. .claude/skills/allowlist/SKILL.md points Claude at
the script so "add Jan to the app" is a one-line request rather than a
procedure to re-derive.

README now points at the script and keeps the explanation of why the
read-modify-write is necessary. Also drops the last :latest reference, in the
manual deploy snippet -- that tag is never published either.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
secretAccessor grants only secretmanager.versions.access -- it does not
imply writing a version or even listing them. So the role we hand new
developers so they can run the app locally is exactly the role that lets
someone run `list` and then fail on `add`, with a raw gcloud PERMISSION_DENIED
as the only clue. Spell the full set out in README, and name the specific
missing role at each point of failure in the script.

Redeploying additionally needs run.developer plus serviceAccountUser on the
runtime service account (959872421018-compute@developer), because deploying a
service that runs as a service account requires acting as it.

Also fixes a race the permission testing turned up: write_version verified by
reading `latest`, which immediately after a write can still return the previous
version -- reporting a successful write as a failure. It now reads back the
exact version it just created, and if that ever does mismatch, says the version
was created rather than implying nothing happened. read_version also keeps
stderr out of the payload, so a gcloud warning can no longer be parsed as an
email address.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

4 participants