fix: address Copilot review of #198 (scope-denied sync result, legacy ref migration write, lockfile) - #203
Conversation
Three follow-ups from the unresolved Copilot review threads on #198: - `readTrustedConfig` swallowed `InvocationScopeDeniedError` and fell back to the remembered/empty config, so a scheduled sync for a company with mappings but no saved plugin-config row silently ran with an empty (or worker-local fallback) config instead of recording the documented operator message. Sync execution now opts into `requireCompanyScope`, and `startSync` turns the rethrown error into `createUnexpectedSyncErrorResult`, which persists `COMPANY_SCOPE_DENIED_SYNC_MESSAGE` (README:380). Settings/registration data paths keep the lenient behavior so the UI can still render an empty config. - `patchPluginConfig` compared the normalized current config against the next config, so a legacy bare-UUID secret ref (which normalizes to the same `{ type: "secret_ref" }` binding the patch produces) made the `githubTokenNeedsConfigSync` migration effect return without POSTing and the host row stayed a bare UUID (`binding_missing`). The equality check now runs against the raw stored shape via the new `hasLegacyPluginSecretRefs` helper, so a legacy row always forces the upgrade write. - Regenerated `package-lock.json` with `npm install --package-lock-only` on Node 24; the SDK range and engines had already been resynced by #199/#202, the root `version` was still on 0.17.0. Adds unit tests for the scheduled scope-denied sync result, the legacy-ref rewrite through `patchPluginConfig`, and `hasLegacyPluginSecretRefs`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved comments remain regarding legacy-ref compatibility and raw scope-denial diagnostics.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This follow-up fixes scope-denied sync reporting, legacy secret-reference migration, and lockfile metadata.
Changes:
- Prevents fallback-token use when company scope is denied.
- Forces migration writes for legacy secret references.
- Adds regression tests and updates
package-lock.jsonto0.17.1.
File summaries
| File | Description |
|---|---|
tests/plugin.spec.ts |
Adds regression coverage for sync errors and secret-ref migration. |
src/worker.ts |
Handles denied company scope during scheduled syncs. |
src/ui/plugin-config.ts |
Detects legacy secret-reference formats. |
src/ui/index.tsx |
Migrates legacy references through config writes. |
package-lock.json |
Updates package version metadata. |
Review details
Suppressed comments (1)
src/worker.ts:22298
- Passing the original scope-denial exception here also stores its raw message (for example,
company context is required) inerrorDetails. The UI renders that field as “Raw error” (src/ui/index.tsx:8324-8327), so this path still surfaces the host error alongside the requested operator guidance; sanitize or suppress raw diagnostics for scope-denied failures while retainingCOMPANY_SCOPE_DENIED_SYNC_MESSAGE.
return createUnexpectedSyncErrorResult(ctx, trigger, error, targetCompanyId);
- Files reviewed: 4/5 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Copilot review of #203: forcing the legacy -> binding migration write made pre-2026.831 hosts (which reject binding refs with "plugin secret references are disabled") fall into the existing strip-and-retry fallback, and `stripPluginSecretRefConfig` drops the `*TokenRefs` maps entirely — so the old host would lose the bare secret-id row that is the only shape it understands. When the legacy migration is the *only* reason for the write (the normalized config is otherwise unchanged), abandon the write instead of persisting the stripped config, leaving the stored row intact. A patch that also changes other config keys still takes the pre-existing strip-and-retry path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Note on the suppressed review comment ( |
Follow-up to #198: addresses the three unresolved GitHub Copilot review threads left on that PR after it was merged.
1.
src/worker.ts— scope-denied sync must surface the operator messagereadTrustedConfigcaughtInvocationScopeDeniedErrorand fell back to the remembered (or empty) config. For a scheduled sync target whose company has mappings but no saved plugin-config row,startSynctherefore never reachedcreateUnexpectedSyncErrorResult, soCOMPANY_SCOPE_DENIED_SYNC_MESSAGEwas never persisted — and with a worker-local fallback token the sync could even continue silently.readTrustedConfig/getResolvedConfiggained arequireCompanyScopeoption: when the host denies the company scope and it never delivered a config for that company, the error is rethrown.startSyncopts in and converts the rethrown scope-denied error intocreateUnexpectedSyncErrorResult(...), which records the message already documented inREADME.md:380.2.
src/ui/index.tsx— legacy bare-UUID refs never got migratedreadPluginConfignormalized legacy bare-UUID refs into{ type: "secret_ref" }beforepatchPluginConfigcompared current vs next, so thegithubTokenNeedsConfigSyncmigration effect saw an identical object, returned early, and the host row stayed a bare UUID (binding_missingforever).hasLegacyPluginSecretRefs()insrc/ui/plugin-config.ts.patchPluginConfignow reads the raw row (readRawPluginConfig) and only takes the no-op shortcut when the stored row is already in binding form, so a legacy row always forces the upgrade write.3.
package-lock.jsonThe repo does keep an npm lockfile (Renovate maintains it alongside
pnpm-lock.yaml), so it was regenerated rather than removed. The SDK range (^2026.831.1) andengines.node(>=24.21.0) drift Copilot flagged was already resolved onmainby #199 and #202; only the rootversionwas still0.17.0. Regenerated withnpm install --package-lock-onlyon Node 24.21.0 — the only resulting delta is the version bump to0.17.1.Tests
scheduled sync records the scope-denied operator message for a company the host refuses to scope and never configured— also asserts no GitHub request is made with a worker-local fallback token, that the settings data path stays lenient, and that the sync succeeds once the host delivers the config viaconfigChanged.patchPluginConfig rewrites a legacy bare-UUID secret ref as a binding even when the normalized config is unchanged— plus the follow-up no-op assertion once the row is a binding.hasLegacyPluginSecretRefs only flags secret refs stored as bare secret-id strings.pnpm typecheck,pnpm test(337 passing) andpnpm buildare green locally on Node 24.21.0.