Repository navigation
feat(packs): script host context — pack store and change-request reads (T2) - #443
Conversation
…s (T2) Recipe scripts get two host members on ScriptHandlerCtx: - ctx.store: the recipe's own pack document store (T1), present iff the recipe is a pack recipe whose pack registered persistence (store:v1); bound to that pack id, so one pack can never see another's documents. - ctx.changeRequests.detail/diff: read-only change requests backed by PullRequestService, present iff the recipe declares integration.read. Only repositories the service resolves for the run's project (own remote + linked repositories, new PullRequestService.projectRepositories) are readable; anything else is refused with the typed ChangeRequestScopeError before any provider call. Diff pages are whole files, bounded by page size and characters, with an opaque cursor. Built per run at launch and rebuilt on boot rehydration from the run row, so a restored run keeps exactly the access it launched with. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e script host Effect diagnostics on main now reject global Error values in a failure channel and unknown error channels; a bad number or diff cursor fails with ChangeRequestInputError, and the pack store adapter carries T3TeamPackDocumentStoreError. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Thread transfer impact
This comment will update automatically after the next completed run. |
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0e6df0c238
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const next = | ||
| nextOffset < files.length | ||
| ? encodeDiffCursor({ slice: position.slice, offset: nextOffset }) | ||
| : slice.nextCursor === null | ||
| ? undefined | ||
| : encodeDiffCursor({ slice: slice.nextCursor, offset: 0 }); |
There was a problem hiding this comment.
Reject repeated provider diff cursors
When a provider returns the same non-null nextCursor for consecutive slices, this code reissues an identical encoded next, so a script following while (page.next) repeatedly fetches the same page and can make unbounded provider calls. This failure mode is already handled by the seen-cursor and page-limit guards in loadFullDiff; apply an equivalent guard here, or at minimum reject slice.nextCursor === position.slice, rather than returning the cursor again.
AGENTS.md reference: AGENTS.md:L41-L47
Useful? React with 👍 / 👎.
Recipe scripts could only reach files,
fetchand tools. Pack logic needs its own durable store (T1) and a provider-neutral, read-only view of the change requests it works on, without holding provider tokens or callinggh/glabitself. This is seam S4, the T2 subset.What scripts get
ScriptHandlerCtxgains two optional members. Each is built per run and is present only when the run is entitled to it:ctx.store: ScriptPackStore: the recipe's own pack store (T3TeamPackDocumentStore.forPack), offeringget,list,insertOrGet,put(CAS) andincrement. It is present only when the recipe directory is a registered pack recipe root and that pack registered persistence (which requiresstore:v1). No method takes a pack id, so pack A cannot see pack B.ctx.changeRequests: ChangeRequestReader:detail(ref)anddiff(ref, { cursor?, pageSize?, commit? }), backed byPullRequestService, so every provider it reads works here too. It is present only when the recipe's own declared tool groups includeintegration.read(read from the recipe, never the request).ref = { repository, number, host? }, never a URL. Only repositoriesPullRequestServiceresolves for the run's project are readable: the project's own remote plus its linked repositories, via the newPullRequestService.projectRepositories(projectId). Anything else fails with the typedChangeRequestScopeErrorbefore any provider call. This is stricter than a host-qualifiedPullRequestRef, which may route to any repository on that host.pageSizeof them (default 20, max 100) and at most 256,000 characters. The cursor is opaque: the provider's own slice cursor plus a file offset, so re-reading a slice is aPullRequestServicecache read. The page is returned as data:{ kind: "change-request-diff-page", repository, number, patch, fileCount, truncated, next? }.ChangeRequestInputError.Wiring
WorkflowRunOptions.scriptHostis spread into the script context int3team-sdk.workflowRunner.ts.T3TeamScriptHost.forRun({ projectId, recipePath, toolGroups })(t3team-scriptHostContext.ts) in two places:recipe_pathand persisted host-tool grant, so a restored run keeps exactly the access it launched with and gains none.server.ts. The route requires the service, so typecheck proves it is provided.PullRequestService.tsandserver.tswere already inallowedModifiedFiles.Deviations from the S4 sketch
diff(ref, { cursor, pageSize, commit })instead ofdiff(ref, { base, head }, cursor).PullRequestServicediffs the whole change request or a single commit, not an arbitrary base..head range.headShaandheadRepository.baseSha,isCrossRepositoryandauthorAssociationwould need contract and provider-adapter changes, so they are not added.fileAt,blobShasandproject.linkedRepositoriesare left to T12, as planned.Verification (local; this fork's CI skips test jobs)
vp test run(run serially):t3team-scriptHostContext.test.ts: 5/5. A real run vialaunchWorkflowRecipewith a script that reads detail, pages the diff across two provider slices and writes the pack store, all against a fakePullRequestService. Also covers: refusal of an unlinked repository or the right selector on the wrong host, with zero provider calls; store isolation between two packs; members absent withoutstore:v1, without a pack, or withoutintegration.read.t3team-scriptHostDiffPage.test.ts: 4/4.tsc --noEmitclean forapps/serverandpackages/t3team-sdk. Targetedvp lintandvp fmtclean.node t3team-additive-guard.mjspasses.🤖 Generated with Claude Code