Skip to content

RFAI-11: Ambient channel hardening — VS Code extension + voice prototype#1079

Open
Chris0Jeky wants to merge 28 commits into
mainfrom
feat/983-ambient-channel-hardening
Open

RFAI-11: Ambient channel hardening — VS Code extension + voice prototype#1079
Chris0Jeky wants to merge 28 commits into
mainfrom
feat/983-ambient-channel-hardening

Conversation

@Chris0Jeky
Copy link
Copy Markdown
Owner

Summary

  • ADR-0033: Documents decision to harden VS Code extension as the production ambient capture channel over desktop voice, based on testability, privacy surface, and developer workflow fit
  • Backend: Adds VsCodeExtension = 11 to CaptureSource enum, registers "vscode" as supported source surface, adds VsCode to LlmRequestSourceSurface
  • VS Code extension prototype (extensions/vscode-taskdeck/): Capture-selection and capture-file commands, right-click context menu, Ctrl+Shift+T keybinding, status bar indicator, git remote hash for workspace identification, VS Code SecretStorage for token management
  • PWA voice prototype: useVoiceCapture composable provides browser-local voice capture with explicit webkitSpeechRecognition rejection (streams audio to Google)
  • Frontend: Mirrors VsCodeExtension in CaptureSource type union

Testing

  • 3 new backend theory tests: "vscode" accepted as source surface (case-insensitive)
  • 7 new frontend tests: voice capture supported/unsupported/webkit-rejection, start/stop/transcript/error
  • Extension context module test: buildCaptureText formatting
  • All 3290 frontend tests pass, backend builds clean

Closes #983

Stacked on #1078 (feat/982-pwa-share-target)

Test plan

  • Backend: dotnet test --filter CaptureRequestContractTests passes with new VsCode surface tests
  • Frontend: npx vitest --run -t "useVoiceCapture" — 7 tests pass
  • Extension: cd extensions/vscode-taskdeck && npx tsc --noEmit compiles clean
  • Verify ADR-0033 is indexed in docs/decisions/INDEX.md
  • Verify CaptureSource.VsCodeExtension flows through capture API without errors

Choose VS Code extension over desktop voice for ambient channel
hardening based on testability, smaller privacy surface, and
developer workflow fit. Add VsCodeExtension (= 11) to CaptureSource
enum, register "vscode" as supported source surface, and add VsCode
to LlmRequestSourceSurface.
Minimal extension with capture-selection and capture-file commands,
right-click context menu, Ctrl+Shift+T keybinding, status bar
indicator, and git remote hash for workspace identification. Uses
VS Code SecretStorage for token management. No marketplace
publishing (prototype only per ADR-0033).
Backend: 3 new theory tests verify "vscode" is accepted as a
supported source surface (case-insensitive).

Frontend: useVoiceCapture composable provides browser-local voice
capture, explicitly rejecting webkitSpeechRecognition (streams to
Google). 7 unit tests cover supported/unsupported/webkit-rejection,
start/stop/transcript/error flows.

Extension: context.test.ts covers buildCaptureText formatting.
Copilot AI review requested due to automatic review settings May 16, 2026 17:47
@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a75a0efca5

ℹ️ 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".

Comment thread extensions/vscode-taskdeck/package.json Outdated
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"lint": "eslint src",
"test": "node ./out/test/runTest.js"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point extension test script to a real runner

The test script targets ./out/test/runTest.js, but this commit does not add a corresponding src/test/runTest.ts (or any checked-in runner that would produce that file), so npm test will fail with a module-not-found error in a clean checkout. This leaves the new extension without a runnable automated test entrypoint and blocks CI/local verification of the feature.

Useful? React with 👍 / 👎.

Comment on lines +61 to +63
req.on('error', (err) => reject(new Error(`Network error: ${err.message}`)));
req.write(body);
req.end();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add timeout handling for capture HTTP requests

This request path only listens for error events and never sets a request timeout, so if the configured API host accepts the connection but does not respond (for example, a hung proxy or blackholed route), the promise can remain pending for a long time and the command appears frozen with the UI stuck in "Sending...". Adding explicit timeout+abort handling would make failures deterministic and recoverable.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Adversarial Review R1 — RFAI-11: Ambient Channel Hardening

CRITICAL

C-1: node_modules committed to repository — ALREADY FIXED in commit 0a8c405a. Verified not tracked.

C-2: Command injection / NTLM relay via UNC cwd in context.tsexecSync with cwd from workspaceFolder.uri.fsPath could be a UNC path on Windows, enabling NTLM credential relay to attacker SMB shares.

HIGH

H-1: HTTP client has no timeout — hangs indefinitelyclient.ts http.request has no timeout option. Non-responsive API causes permanent "Sending..." state.

H-2: statusBarItem accessed after disposesendCapture's setTimeout fires after deactivate, writing to disposed StatusBarItem.

H-3: sourceSurface round-trip not tested — No test exercises LlmRequestSourceSurface.VsCode → serialized "vscode"IsSupportedSourceSurface validation chain.

H-4: onerror/onend race overwrites error state — Web Speech API fires onend after onerror, resetting status to 'idle'. Test passes falsely because it never triggers onend.

MEDIUM

M-1: Empty capture text sent to backendcaptureFileContext() can produce empty string from files outside workspaces.

M-2: Unvalidated apiUrl stored — Empty string accepted, causes confusing errors on next capture.

M-3: Extension tests not runnable in CIpackage.json test script points to nonexistent ./out/test/runTest.js.

M-4: Unbounded response buffering — No limit on response body size in HTTP client.

M-5: recognition not nulled on error — Stale reference after error state.

LOW

L-1: VsCode vs VsCodeExtension naming asymmetry — Different enum names across layers, future maintenance trap.

L-2: Empty token stored — Empty Enter in token prompt saves "" to SecretStorage.

L-3: ADR-0033 filed as Accepted — Per convention should be Proposed until merged.

L-4: VsCodeExtension missing from IsTranscriptSource theory — New enum value not covered in non-transcript test.

L-5: Keybinding Ctrl+Shift+T conflicts with Reopen Tab — Shadows common VS Code binding.

L-6: No consent hook on voice composable — No explicit consent gate in API surface for voice capture.

Fix Plan

All 17 findings will be addressed (C-1 already done). Fixes incoming.

C-2: Reject UNC paths in getGitRemoteHash to prevent NTLM relay
H-1: Add 10s request timeout + timeout event handler to HTTP client
H-2: Guard statusBarItem writes with disposed flag
H-3: Add 4 source surface serialization pinning tests
H-4: Preserve error state when onend fires after onerror
M-1: Guard empty capture text in captureFileContext
M-2: Validate URL format before storing apiUrl config
M-3: Fix extension test script to compile and run context tests
M-4: Limit response body buffering to 64KB
M-5: Null recognition reference in onerror handler
L-1: Document naming asymmetry with code comment
L-2: Reject empty token in setToken command
L-3: Mark ADR-0033 as Proposed (pre-merge convention)
L-4: Add VsCodeExtension to IsTranscriptSource theory test
L-5: Change keybinding to Ctrl+Shift+Alt+T (avoid Reopen Tab)
L-6: Add consent acknowledgment gate to useVoiceCapture
C-R2-1: Fix test script — use tsc -p ./ (emit) instead of --noEmit;
  refactor context.test.ts to inline the pure function to avoid vscode
  module dependency outside extension host.
H-R2-1: Add res.on('error') no-op handler before res.destroy() to
  prevent unhandled stream error event.
H-R2-2: Guard stopListening() with status !== 'error' check,
  consistent with the onend handler fix from R1.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c990472479

ℹ️ 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".

}
},
"scripts": {
"compile": "tsc -p ./",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Emit test artifact before requiring compiled test file

The test script still fails in a clean checkout because it type-checks with npx tsc --noEmit and then immediately requires ./out/context.test.js, but --noEmit does not produce out/*. Fresh evidence versus earlier review threads: the current script now points to out/context.test.js, yet it still never compiles before executing it, so npm test can fail with module-not-found unless npm run compile was run manually beforehand.

Useful? React with 👍 / 👎.

Comment on lines +117 to +119
try {
new URL(value);
} catch {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject non-HTTP schemes in API URL configuration

The URL validation only checks new URL(value) but does not enforce http:/https: schemes, so values like ftp://... are accepted by setApiUrl even though the UX says “valid HTTP(S) URL.” Later createCapture routes any non-https: protocol through http.request, which makes captures fail at runtime with a network/protocol error until the user fixes config manually.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Adversarial Review R2 — Post-Fix Verification

R1 Fix Verification

Finding Fix Verified
C-1: node_modules Already fixed in 0a8c405 PASS
C-2: UNC path NTLM relay isLocalAbsolutePath() rejects UNC on Windows PASS
H-1: No HTTP timeout timeout: 10_000 + req.on('timeout') handler PASS
H-2: Post-dispose write disposed flag + setStatusText() guard PASS
H-3: Source surface round-trip 4 theory tests pinning all enum serializations PASS
H-4: onerror/onend race onend guards status !== 'error'; recognition = null in onerror PASS
M-1: Empty capture text !captureText.trim() guard in captureFileContext PASS
M-2: Unvalidated apiUrl new URL(value) validation + empty rejection PASS
M-3: Tests not runnable Fixed in R2 — see C-R2-1 below PASS (after R2 fix)
M-4: Unbounded response 64KB limit + res.destroy() PASS (after R2 fix)
M-5: Recognition not nulled recognition = null in onerror PASS
L-1: Naming asymmetry Documented with code comment PASS
L-2: Empty token !value.trim() rejection PASS
L-3: ADR status Changed to Proposed PASS
L-4: Missing InlineData VsCodeExtension added PASS
L-5: Keybinding conflict Changed to Ctrl+Shift+Alt+T PASS
L-6: Consent hook consentAcknowledged ref + acknowledgeConsent() + gate PASS

R2 New Findings (all fixed in 385fb527)

Finding Severity Fix
C-R2-1: --noEmit prevents test execution CRITICAL Use tsc -p ./ (emit), inline pure function in test to avoid vscode dep
H-R2-1: res.destroy() unhandled error event HIGH Add res.on('error', () => {}) before destroy
H-R2-2: stopListening() erases error state HIGH Guard with status !== 'error' check

Verdict: PASS — All R1 + R2 findings fixed and verified.

Test results:

  • Frontend: 3292 tests pass
  • Backend (relevant): 49 tests pass (CaptureRequestContract + AttributionMapper)
  • Extension: 10 context tests pass, typecheck clean
  • Extension test script (npm test) now works end-to-end

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 385fb52740

ℹ️ 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".

let data = '';
let bytesReceived = 0;

res.on('error', () => {});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject aborted capture responses instead of swallowing them

The response stream error handler is currently a no-op, so a connection drop after headers can leave this promise unresolved: end never fires, no rejection is emitted, and the command can stay stuck in the "Sending..." state indefinitely. This is especially reproducible with flaky proxies or mid-stream socket resets. Handle res failures (error/aborted/close) by rejecting so the UI can recover.

Useful? React with 👍 / 👎.

@@ -0,0 +1,106 @@
// Test the pure buildCaptureText function without importing vscode-dependent modules.
// We re-implement the type and function inline to avoid the vscode import chain.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Test the real formatter instead of a copied implementation

This test file duplicates buildCaptureText inline instead of importing the production function, so the test can pass even when src/context.ts regresses. Any future change that updates only one copy will silently invalidate coverage and let formatting bugs ship. The test should execute the actual exported formatter (or a helper extracted from it), not a reimplementation.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Cleanup Fix Evidence — 2026-05-16

Addressed the current CI and bot-review blockers in commit 5a689d9f:

  • Added Web Speech API type declarations and used window.SpeechRecognition constructor narrowing so frontend typecheck/build no longer fail on SpeechRecognition.
  • Hardened first-run local config writes with bounded retry around the atomic replace to absorb transient Windows file locks seen in API integration CI.
  • Reworked extension response handling to reject on response error/aborted/premature close instead of swallowing stream failures.
  • Extracted buildCaptureText into contextFormatter.ts and updated the extension test to import the production formatter rather than duplicating it.
  • Enforced http:/https: protocols in VS Code extension API URL configuration.

Verification run locally from .worktrees/pr-1079:

  • npm run typecheck in frontend/taskdeck-web — pass
  • npm run build in frontend/taskdeck-web — pass
  • npx vitest --run src/tests/composables/useVoiceCapture.spec.ts — 9 passed
  • npm test in extensions/vscode-taskdeck — 10 passed
  • npm run compile in extensions/vscode-taskdeck — pass
  • dotnet test backend/tests/Taskdeck.Api.Tests/Taskdeck.Api.Tests.csproj -c Release --filter FullyQualifiedName~OAuthTokenLifecycleTests.SignalR_AcceptsValidJwt — 1 passed

Waiting for GitHub CI on the pushed head before considering stack retarget/merge readiness.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a689d9f66

ℹ️ 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".

Comment on lines +41 to +43
return new Promise<CaptureResponse>((resolve, reject) => {
const transport = url.protocol === 'https:' ? https : http;
let settled = false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce HTTP(S) scheme before selecting HTTP transport

createCapture falls back to http for any non-https: protocol, so a user-edited taskdeck.apiUrl (for example ftp://... in VS Code settings JSON) will bypass the command-level validator and fail at runtime with a protocol/network error instead of a deterministic config error. Fresh evidence versus earlier review threads: setApiUrl now checks protocol, but this path still reads raw config and never rejects non-HTTP schemes before http.request is chosen.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Follow-up fix pushed for the failing frontend lint lane.

Fix commit:

  • 90b3a8fa removes the unused global Web Speech value declarations and keeps the type surface scoped to window.SpeechRecognition / window.webkitSpeechRecognition, which is how the app code uses it.

Local verification:

  • npm run lint - passed (6 pre-existing accessibility warnings, 0 errors)
  • npm run typecheck - passed
  • npm run build - passed
  • npx vitest --run src/tests/composables/useVoiceCapture.spec.ts - passed, 9 tests

Fresh CI is pending after the push.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Follow-up merge pushed to pick up #1078's Windows PaperReviewView test fix on this stacked branch.

Update:

Local verification:

  • npx vitest --run src/tests/views/paper/review/PaperReviewView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/composables/useVoiceCapture.spec.ts - passed, 48 tests
  • npm run lint - passed (6 pre-existing accessibility warnings, 0 errors)
  • npm run typecheck - passed
  • npm run build - passed

Fresh CI is pending after the push.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b660edf28a

ℹ️ 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".

if (!isLocalAbsolutePath(cwd)) return null;

try {
const remote = execSync('git remote get-url origin', {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid shell-based git lookup in workspace context

This call executes 'git remote get-url origin' through execSync with cwd set to the opened workspace, which allows command resolution from the workspace directory. On Windows, a repo containing a malicious git.cmd/git.bat can be executed when capture runs, turning a normal capture action into arbitrary code execution. Use a non-shell invocation (execFile/spawn with shell: false) and explicit args to prevent workspace command hijacking.

Useful? React with 👍 / 👎.

Comment on lines +60 to +61
recognition = new SpeechRecognitionCtor()
recognition.continuous = false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent overlapping voice recognition sessions

startListening() always creates a new SpeechRecognition instance without checking whether one is already active. If the user triggers start twice before onend, the original session keeps running but its reference is lost, so stopListening() can no longer stop it and multiple callbacks can race to mutate transcript/status. Guard against re-entry (or stop the existing instance) before assigning a new recognizer.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdef719543

ℹ️ 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".

} catch {
throw new Error(`Invalid API URL: "${apiUrl}". Run "Taskdeck: Set API URL" to fix.`);
}
const url = new URL('/api/capture/items', baseUrl);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve API base path when building capture endpoint

This always constructs the request URL from an absolute path (/api/capture/items), which drops any path segment configured in taskdeck.apiUrl (for example https://host/taskdeck becomes https://host/api/capture/items). Because parseTaskdeckApiBaseUrl currently accepts such URLs, reverse-proxy/subpath deployments will consistently post to the wrong endpoint and capture will fail until users reconfigure to an origin-only URL.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Current-head fix/propagation evidence at 16c30fd6:

Verification run locally:

  • .worktrees/pr-1079/frontend/taskdeck-web: npx vitest --run src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/composables/useVoiceCapture.spec.ts - PASS, 46 tests
  • .worktrees/pr-1079/extensions/vscode-taskdeck: npm test - PASS
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run typecheck - PASS
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run build - PASS

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fresh adversarial review - PR #1079 current head 16c30fd6

MEDIUM

  • extensions/vscode-taskdeck/package.json:14 - activationEvents is still [], while the advertised status-bar capture entry is created only inside activate() in extensions/vscode-taskdeck/src/extension.ts. VS Code can activate contributed commands lazily, but a fresh window will not show the Taskdeck status-bar affordance until the user has already invoked another command path, which breaks the intended ambient capture/discoverability path and leaves the latest Codex bot comment unresolved. Expected fix: add an eager activation event such as onStartupFinished (or another deliberate startup activation strategy) and, if practical, pin it with a manifest-level test/assertion.

Verified Areas

  • Extension API URL parsing: pathful values are rejected; HTTP is limited to localhost, [::1]/::1, or syntactically numeric 127/8 IPv4; http://127.attacker.example is covered by apiUrl.test.ts and rejected.
  • Token handling: setToken() rejects empty trimmed input and stores value.trim() in SecretStorage.
  • Voice capture lifecycle: onresult, onerror, and onend are guarded by recognizer identity, including a stale onend regression test after a stopped previous session.
  • RFAI-10: PWA share-target quick capture with offline queue #1078 share queue propagation: latest ownerless logged-out share queueing and remote-create/local-dequeue split are present in this stacked head.
  • Bot comments: Gemini quota warning and Copilot review error have no actionable code findings; earlier Codex findings are addressed by current code/tests. The current-head Codex activation comment above remains valid.

Commands / Checks Run

  • gh pr view 1079 --json ..., gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh pr view 1079 --comments, gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh issue view 983 --comments - inspected PR body, linked issue, reviews, issue comments, bot comments, and review comments.
  • gh pr diff 1079 --name-only - inspected latest touched surface.
  • npm test in .worktrees/pr-1079/extensions/vscode-taskdeck - PASS, 10 context tests plus apiUrl.test.js.
  • npx vitest --run src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/composables/useVoiceCapture.spec.ts in .worktrees/pr-1079/frontend/taskdeck-web - PASS, 46 tests.
  • npm run typecheck in .worktrees/pr-1079/frontend/taskdeck-web - PASS.
  • gh pr checks 1079 - all completed required checks pass at snapshot; API Integration / API Integration (windows-latest) was still pending.

Verdict: one actionable MEDIUM finding remains; I would not mark #1079 clean until the activation event/discoverability issue is fixed and CI finishes green.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fix evidence for current-head MEDIUM finding at e48df347:

  • MEDIUM: the VS Code extension now activates on onStartupFinished, so the Taskdeck status-bar capture entry is visible in a fresh VS Code window before any command manually activates the extension.
  • Added a manifest-level test that fails if activationEvents drops onStartupFinished again.
  • Latest RFAI-10: PWA share-target quick capture with offline queue #1078 share-target provenance fix is also propagated into this stacked branch.

Verification run locally:

  • .worktrees/pr-1079/extensions/vscode-taskdeck: npm test - PASS
  • .worktrees/pr-1079/frontend/taskdeck-web: npx vitest --run src/tests/public/shareTargetHandler.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/composables/useVoiceCapture.spec.ts - PASS, 49 tests
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run typecheck - PASS
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run build - PASS

Fresh CI is pending on the pushed head.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e48df347e3

ℹ️ 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".

Comment on lines +107 to +110
if (recognition) {
recognition.stop()
recognition = null
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve final transcript when stopping recognition

stopListening() clears recognition immediately after calling stop(), but SpeechRecognition.stop() can deliver the final onresult asynchronously. Because onresult ignores callbacks when recognition !== activeRecognition, users who press stop right after speaking can lose the last recognized phrase and end up with an empty/partial transcript even though recognition succeeded.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fresh adversarial review - PR #1079 current head e48df347

HIGH

  • frontend/taskdeck-web/public/share-target-handler.js:50 - the share-target provenance gate still trusts POST /capture/share when Sec-Fetch-Site is absent. A cross-site form POST with no Fetch Metadata header, or any client/proxy path that strips that header and omits Origin, will still reach formData(), stage attacker-controlled content in taskdeck-share-target, and redirect into the logged-in user's share flow. This only blocks the modern explicit cross-site case; it does not fail closed when the trusted-provenance signal is missing. Expected fix: require an explicit trusted signal for this write surface, for example Sec-Fetch-Site: none for OS share target or same-origin for first-party submits, and reject missing/ambiguous metadata unless a separate first-party nonce/handshake is added. Add a regression test proving a request with no Origin and no Sec-Fetch-Site is rejected before cache writes.

Verified Areas

  • Activation/discoverability: extensions/vscode-taskdeck/package.json now includes activationEvents: ["onStartupFinished"], and extensions/vscode-taskdeck/src/manifest.test.ts pins it, so the status-bar capture entry should be created in a fresh VS Code window.
  • Extension API URL hardening: parseTaskdeckApiBaseUrl() rejects pathful base URLs, embedded credentials, non-HTTP(S) protocols, plaintext non-loopback hosts, and http://127.attacker.example; covered by apiUrl.test.ts.
  • Token handling: setToken() now stores value.trim() after rejecting empty trimmed input.
  • Voice lifecycle: useVoiceCapture() guards onresult, onerror, and onend by active recognizer identity, with a stale onend regression test.
  • RFAI-10: PWA share-target quick capture with offline queue #1078 propagation: ownerless logged-out share queueing, owner-scoped replay/counts, replay claims, and remote-create/local-dequeue split are present in this head.
  • Existing PR comments/reviews/bots inspected: Gemini quota warning has no actionable code finding; Copilot review failed without code findings; prior Codex comments for extension test runner, HTTP timeout, response buffering, API URL validation, token trimming, stale voice callbacks, and activationEvents are addressed by current code/tests. The remaining issue is the incomplete RFAI-10: PWA share-target quick capture with offline queue #1078 share-target provenance hardening above.

Commands / Checks Run

  • powershell -File scripts/worktree_guard.ps1 in .worktrees/pr-1079 - PASS.
  • gh pr view 1079 --json number,title,body,headRefName,headRefOid,baseRefName,mergeable,state,comments,reviews,commits,files,statusCheckRollup,author,url - inspected PR body, head e48df347, reviews, comments, files, and checks.
  • gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/reviews --paginate, gh issue view 983 --comments - inspected all issue comments, review comments, bot comments, and linked issue context.
  • gh pr diff 1079 --name-only and gh pr diff 1079 --patch - inspected latest touched surface.
  • npm test in .worktrees/pr-1079/extensions/vscode-taskdeck - PASS, 10 context tests plus API URL and manifest tests.
  • npx vitest --run src/tests/public/shareTargetHandler.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/composables/useVoiceCapture.spec.ts in .worktrees/pr-1079/frontend/taskdeck-web - PASS, 49 tests.
  • npm run typecheck in .worktrees/pr-1079/frontend/taskdeck-web - PASS.
  • dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release --filter "FullyQualifiedName~CaptureRequestContractTests|FullyQualifiedName~LlmRequestAttributionMapperTests" - PASS, 49 tests; existing CS1998 warnings only.
  • gh pr checks 1079 - all completed checks pass at snapshot; API Integration / API Integration (windows-latest) remains pending.
  • git status --short in .worktrees/pr-1079 - clean.

Verdict: not clean / not merge-ready. The activationEvents fix is verified, and the #1079-specific URL/token/voice findings remain fixed, but the propagated #1078 share-target provenance guard still has a HIGH fail-open gap when provenance headers are absent.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Follow-up fix evidence at 86024687:

Verification run locally:

  • .worktrees/pr-1079/frontend/taskdeck-web: npx vitest --run src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts src/tests/composables/useVoiceCapture.spec.ts - PASS, 52 tests
  • .worktrees/pr-1079/extensions/vscode-taskdeck: npm test - PASS
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run typecheck - PASS
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run build - PASS

Fresh CI is pending on the pushed head.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fresh adversarial review - PR #1079 current head 86024687

MEDIUM

  • frontend/taskdeck-web/src/composables/useVoiceCapture.ts:106 - stopListening() calls recognition.stop() and immediately clears the active recognizer reference. The onresult handler only accepts callbacks while recognition === activeRecognition, so a final result delivered asynchronously after SpeechRecognition.stop() is ignored. That can drop the last spoken phrase when a user presses stop right after speaking, leaving an empty or partial transcript even though recognition succeeded. This is the same risk raised by the latest Codex bot comment and it is still present at head 86024687. Expected fix: keep the active recognizer identity valid until onend (or use an explicit stopping state) so final onresult callbacks from the stopped recognizer are still accepted, then clear it in onend; add a regression test that calls stopListening(), then delivers a final result before onend, and asserts the transcript is preserved.

Verified areas

Commands / checks run

  • powershell -File scripts/worktree_guard.ps1 in .worktrees/pr-1079 - PASS.
  • gh pr view 1079 --json ... - inspected PR metadata/body/head/files/checks at 86024687.
  • GitHub MCP _fetch_pr_comments plus gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/reviews --paginate - inspected comments/reviews/bots.
  • gh issue view 983 --comments --json number,title,state,body,comments,url - inspected linked issue context.
  • gh pr diff 1079 --name-only - inspected latest touched surface.
  • npm test in .worktrees/pr-1079/extensions/vscode-taskdeck - PASS, 10 context tests plus API URL and manifest tests.
  • npx vitest --run src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts src/tests/composables/useVoiceCapture.spec.ts in .worktrees/pr-1079/frontend/taskdeck-web - PASS, 52 tests.
  • npm run typecheck in .worktrees/pr-1079/frontend/taskdeck-web - PASS.
  • dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release --filter "FullyQualifiedName~CaptureRequestContractTests|FullyQualifiedName~LlmRequestAttributionMapperTests" - PASS, 49 tests; existing CS1998 warnings only.
  • gh pr checks 1079 - completed checks were passing at snapshot; required API Integration ubuntu/windows, backend unit Windows, and frontend unit Windows were still pending.
  • git status --short in .worktrees/pr-1079 - clean.

Verdict: not clean / not merge-ready. The requested activationEvents fix and final propagated #1078 headerless POST/re-auth fixes are verified, but the current head still has one MEDIUM voice-capture data-loss bug and CI is not fully green yet.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Follow-up fix evidence at 35f2823e:

  • MEDIUM: useVoiceCapture.stopListening() no longer makes a final async recognition result impossible to accept. The composable keeps a stopped recognizer in a shutdown slot until onend, accepts its final result when no newer session is active, and still ignores stale stopped-session results once a new recognition session starts.
  • Added regression tests for final result after Stop and stale result suppression after a new session begins.
  • Note: one npm run build attempt was run from the worktree root by mistake and failed with missing package.json; rerun from frontend/taskdeck-web passed.

Verification run locally:

  • .worktrees/pr-1079/frontend/taskdeck-web: npx vitest --run src/tests/composables/useVoiceCapture.spec.ts src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts - PASS, 54 tests
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run typecheck - PASS
  • .worktrees/pr-1079/frontend/taskdeck-web: npm run build - PASS
  • .worktrees/pr-1079/extensions/vscode-taskdeck: npm test - PASS

Fresh CI is pending on the pushed head.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fresh adversarial review - PR #1079 current head 35f2823e

Verdict: no findings / review-clean at this head. I do not see any remaining CRITICAL, HIGH, MEDIUM, or LOW code findings after the latest voice final-result-after-stop fix.

Verified areas

  • PR body, linked issue RFAI-11: Ambient channel hardening decision and prototype #983, latest file list/diff surface, issue comments, inline review comments, review submissions, and bot comments were inspected. Gemini's quota notice and Copilot's failed review do not contain actionable code findings; prior Codex review findings are addressed by current code/tests.
  • Voice final-result-after-stop fix: useVoiceCapture() keeps a stopped recognizer in a shutdown slot until onend, accepts its final onresult when no newer session is active, and still suppresses stale results once a new session starts. Regression coverage is present for both final result after Stop and stale stopped-session result suppression.
  • Activation/discoverability fix: extensions/vscode-taskdeck/package.json includes activationEvents: ["onStartupFinished"], and extensions/vscode-taskdeck/src/manifest.test.ts pins that behavior.
  • Extension hardening remains in place: API URLs reject paths, embedded credentials, non-HTTP(S) protocols, plaintext non-loopback hosts, and http://127.attacker.example; token storage trims the submitted token before writing to SecretStorage.
  • Propagated RFAI-10: PWA share-target quick capture with offline queue #1078 security/share fixes remain present: headerless POST /capture/share fails closed unless same-origin Origin is present, Sec-Fetch-Site: none OS share-target posts still work, cache writes use unique share IDs, and 401/403 share submissions are preserved locally with owner null for re-auth/replay.
  • Backend source-surface contract coverage for VsCodeExtension/vscode remains covered by filtered application tests.

Commands / checks run

  • powershell -File scripts/worktree_guard.ps1 in .worktrees/pr-1079 - PASS.
  • gh pr view 1079 --json ..., gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/reviews --paginate, gh issue view 983 --comments --json ... - inspected PR/issue/review/bot surfaces.
  • gh pr diff 1079 --name-only, git diff --name-status origin/feat/982-pwa-share-target...HEAD - inspected latest touched surface.
  • npm test in .worktrees/pr-1079/extensions/vscode-taskdeck - PASS.
  • npx vitest --run src/tests/composables/useVoiceCapture.spec.ts src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts - PASS, 54 tests.
  • dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release --filter "FullyQualifiedName~CaptureRequestContractTests|FullyQualifiedName~LlmRequestAttributionMapperTests" - PASS, 49 tests; existing CS1998 warnings only.
  • npm run typecheck in .worktrees/pr-1079/frontend/taskdeck-web - PASS.
  • npm run build in .worktrees/pr-1079/frontend/taskdeck-web - PASS.
  • gh pr checks 1079 - completed checks were passing at snapshot; several required CI jobs were still pending, including backend unit, frontend unit, and API integration lanes.
  • git status --short --branch in .worktrees/pr-1079 - clean.

Residual risk / test gap: I did not perform a manual VS Code host smoke test for the status-bar affordance; verification here is manifest-level plus extension unit/compile coverage. Merge readiness still depends on the pending GitHub CI jobs finishing green.

@Chris0Jeky Chris0Jeky changed the base branch from feat/982-pwa-share-target to main May 17, 2026 13:20
@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Conflict resolution evidence for current head d61c084:

  • Rebased/retargeted stack by merging current origin/main into feat/983-ambient-channel-hardening.
  • Resolved the only conflict in docs/agentic/ORCHESTRATION_STATE.md by preserving main's newer orchestration-state document so the branch does not reintroduce the older pre-merge workflow snapshot.
  • Local verification after the merge:
    • frontend/taskdeck-web: npx vitest --run src/tests/composables/useVoiceCapture.spec.ts src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts — PASS (54 tests)
    • frontend/taskdeck-web: npm run typecheck — PASS
    • frontend/taskdeck-web: npm run build — PASS
    • extensions/vscode-taskdeck: npm test — PASS

Fresh current-head adversarial review is running for d61c084 before merge.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fresh adversarial review - PR #1079 current head d61c0849c603b4b7846363a53205513d7bc5902b

Verdict: not clean / not merge-ready. I found two current-head findings after the retarget/reconciliation with main.

MEDIUM

  • extensions/vscode-taskdeck/src/context.ts:33 - VS Code captures opened outside a workspace include doc.fileName as the fallback relativePath, and contextFormatter.ts then writes that value into the capture body while extension.ts also sends it as titleHint. For untitled/out-of-workspace files this can be an absolute local path such as C:\Users\<name>\..., which leaks local usernames/project layout into Taskdeck storage and any later provider-backed triage. The hardened IDE channel should capture relative workspace context, not full host paths. Expected fix: when workspaceFolder is missing, use a basename-only label or null for path context, and add an extension context/formatter test proving full absolute paths are not emitted for out-of-workspace files.

LOW

  • frontend/taskdeck-web/src/components/inbox/inboxUtils.ts:25 - the new VsCodeExtension source is added to the frontend type union, but sourceLabel() still falls through to String(source). Depending on API enum serialization, Inbox/Paper inbox badges will show either raw VsCodeExtension or numeric 11 instead of a product-facing label such as VS Code. This is a product legibility regression for the new capture channel. Expected fix: add label mappings for VsCodeExtension/11 and cover it with a small source-label test.

Verified areas

  • PR body, linked issue RFAI-11: Ambient channel hardening decision and prototype #983, issue comments, inline review comments, review submissions, bot comments, and prior adversarial review/fix-evidence comments were inspected. Gemini's quota notice and Copilot's failed review contain no actionable code finding; earlier Codex/review findings for extension test runner, HTTP timeout/response handling, API URL validation, token trimming, stale voice callbacks, activationEvents, share-target propagation, and final voice result after stop are addressed in the current code/tests.
  • Merge-conflict resolution was inspected. d61c0849 has origin/main (df9113e98c9017aa5bc37e693449499829191a8b) as its second parent, and docs/agentic/ORCHESTRATION_STATE.md is identical to origin/main after the merge, matching the stated conflict resolution.
  • Current PR diff against origin/main is limited to the ambient-channel surface: backend capture source/source-surface additions, ADR-0033, VS Code extension prototype, voice composable prototype, and related tests/types.

Commands / checks run

  • powershell -File scripts/check-git-env.ps1 - PASS.
  • git status --short --branch - inspected; local checkout is dirty on unrelated tst/1081-composable-coverage-part2, so review used remote refs only.
  • gh pr view 1079 --json number,title,body,state,baseRefName,headRefName,headRefOid,baseRefOid,author,labels,commits,files,additions,deletions,reviewDecision,url,closingIssuesReferences --comments - inspected PR metadata/body/head/files.
  • GitHub MCP _fetch_pr_comments and gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/reviews --paginate, gh pr view 1079 --comments - inspected comments/reviews/bots. One initial MCP fetch used the wrong repo owner and returned 404; retried successfully with Chris0Jeky/Taskdeck.
  • git fetch origin main:refs/remotes/origin/main and git fetch origin main refs/pull/1079/head:refs/remotes/origin/pr/1079 - fetched current refs.
  • gh issue view 983 --comments --json number,title,state,body,comments,url,labels,author - inspected linked issue.
  • gh pr checks 1079 - several required jobs were still pending at snapshot; completed checks shown were passing/skipping as expected.
  • git show --stat --oneline d61c0849c603b4b7846363a53205513d7bc5902b, git show --cc --name-status --oneline d61c0849c603b4b7846363a53205513d7bc5902b, git rev-parse d61c0849^1 d61c0849^2 origin/main origin/pr/1079, git diff --exit-code d61c0849^2 d61c0849 -- docs/agentic/ORCHESTRATION_STATE.md, git merge-base --is-ancestor origin/main origin/pr/1079 - inspected merge/reconciliation state.
  • git diff --name-status --stat --check origin/main...origin/pr/1079 and targeted git diff --unified=... / git show origin/pr/1079:<path> inspections for backend capture contracts, extension files, voice composable/tests, ADR files, and inbox source label handling.

Residual verification caveat: I did not run build/test suites because this was a read-only review in a dirty main checkout; CI for the current head was still in progress at the time of review.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d61c0849c6

ℹ️ 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".

Comment on lines +95 to +97
setTimeout(() => {
setStatusText('$(cloud-upload) Taskdeck');
}, 3000);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize status-bar reset across concurrent captures

sendCapture always schedules an unconditional status reset 3 seconds after each attempt, so overlapping captures can clobber each other’s UI state: if capture A finishes first while capture B is still running, A’s timer flips the status bar back to $(cloud-upload) Taskdeck even though B is still Sending... (or may later fail). This makes the extension report an idle/success-looking state during an in-flight request and can hide useful failure feedback for the active capture. Track the latest request (or an in-flight counter) before resetting the status text.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fix evidence for review comment #1079 (comment) at head 6acab91:

  • MEDIUM absolute-path leak: fixed in 6acab91 by replacing the out-of-workspace VS Code fallback from doc.fileName to a safe document label. Workspace files still use workspace-relative paths; out-of-workspace local files use only the basename; untitled/non-file documents avoid local directory paths. Added extension regression coverage.
  • LOW source label: fixed in 6acab91 by mapping MarkdownImport, WebClip, ShareTarget, BrowserExtension, and VsCodeExtension in inbox source labels. Added frontend unit coverage for string and numeric enum values.

Verification:

  • extensions/vscode-taskdeck: npm test — PASS (13 context tests plus apiUrl and manifest tests)
  • frontend/taskdeck-web: npx vitest --run src/tests/components/inbox/inboxUtils.spec.ts src/tests/composables/useVoiceCapture.spec.ts src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts — PASS (64 tests)
  • frontend/taskdeck-web: npm run typecheck — PASS
  • frontend/taskdeck-web: npm run build — PASS

Fresh current-head re-review requested for 6acab91 before merge.

Copy link
Copy Markdown
Owner Author

Fresh adversarial re-review - PR #1079 head 6acab9176f3f670855cc24b53c3a2ce838aaf45b

Verdict: not clean / not merge-ready. The two findings from comment #1079 (comment) are fixed, but one existing bot-review finding remains valid at the current pushed head.

LOW

  • extensions/vscode-taskdeck/src/extension.ts:95 - sendCapture() still schedules an unconditional status-bar reset for every capture attempt. If two captures overlap, the earlier capture's timer can reset the status bar to $(cloud-upload) Taskdeck while the later capture is still sending, or can clear the later failure/success state before its own feedback window has elapsed. This is the same actionable Codex inline finding at RFAI-11: Ambient channel hardening — VS Code extension + voice prototype #1079 (comment). Expected fix: track a monotonically increasing capture/reset token or in-flight counter and only reset when the timer belongs to the latest completed capture; add extension test coverage if practical.

Verified fixed from #issuecomment-4470820206

  • MEDIUM absolute-path leak: fixed. getSafeDocumentLabel() now uses workspace-relative labels when available, basename-only labels for out-of-workspace local files, and non-path labels for untitled/non-file documents. context.test.ts covers the Windows absolute-path case and asserts local directory names are omitted.
  • LOW source label: fixed. sourceLabel() now maps VsCodeExtension and numeric 11 to VS Code, with inboxUtils.spec.ts covering both forms.

Existing PR discussion reviewed

  • PR body, linked issue RFAI-11: Ambient channel hardening decision and prototype #983, issue comments, inline review comments, review submissions, and bot comments were inspected with GitHub MCP and gh.
  • Gemini quota and Copilot review-error comments contain no actionable code findings.
  • Prior Codex/review findings for extension test runner, HTTP timeout/response handling, API URL validation, token trimming, stale voice callbacks, activation events, share-target propagation, final voice result after stop, absolute-path leakage, and source labels are addressed in current code/tests.

Commands / checks run

  • powershell -File scripts/check-git-env.ps1 - PASS.
  • git status --short --branch - inspected; local checkout is dirty on unrelated tst/1081-composable-coverage-part2, so review used remote refs only.
  • gh pr view 1079 --repo Chris0Jeky/Taskdeck --json number,title,headRefName,headRefOid,baseRefName,baseRefOid,author,state,isDraft,body,url - confirmed head SHA.
  • gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/reviews --paginate, and GitHub MCP _fetch_pr_comments - inspected comments/reviews/bot feedback. Initial gh api attempts used the wrong owner path and returned 404, then were rerun successfully against Chris0Jeky/Taskdeck.
  • git fetch origin main:refs/remotes/origin/main refs/pull/1079/head:refs/remotes/origin/pr/1079 - fetched current refs.
  • gh issue view 983 --repo Chris0Jeky/Taskdeck --comments --json number,title,state,body,comments,url,labels,author - inspected linked issue.
  • gh pr diff 1079 --repo Chris0Jeky/Taskdeck --name-only, git diff --check origin/main...origin/pr/1079, git diff --name-status --stat origin/main...origin/pr/1079, and targeted git show origin/pr/1079:<path> inspections for extension context/status handling and inbox labels.
  • git merge-base --is-ancestor origin/main origin/pr/1079 - PASS.
  • gh pr checks 1079 --repo Chris0Jeky/Taskdeck - completed checks were passing/skipping as expected; several required CI jobs were still pending at snapshot.

Residual verification caveat: I did not run local build/test suites because this was a read-only review from a dirty checkout; this review is based on remote-ref source inspection plus current GitHub check status.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fix evidence for re-review comment #1079 (comment) at head 20848e6:

  • LOW overlapping status reset: fixed in 20848e6 by adding a generation guard for VS Code capture status resets. Older send attempts can no longer reset the status bar after a newer capture has started.
  • Added extension regression coverage for stale reset generations.

Verification after the fix:

  • extensions/vscode-taskdeck: npm test — PASS (context, apiUrl, manifest, status reset tests)
  • frontend/taskdeck-web: npx vitest --run src/tests/components/inbox/inboxUtils.spec.ts src/tests/composables/useVoiceCapture.spec.ts src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts — PASS (64 tests)
  • frontend/taskdeck-web: npm run typecheck — PASS
  • frontend/taskdeck-web: npm run build — PASS

Fresh current-head re-review requested for 20848e6 before merge.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fresh adversarial re-review - PR #1079 head 20848e6191e766760f5ca05e6e6a73cee0254b3c

Verdict: not clean / not merge-ready. The absolute-path and source-label findings from comment #1079 (comment) are fixed, and the delayed idle reset from comment #1079 (comment) is guarded. One overlapping-capture status path remains.

LOW

  • extensions/vscode-taskdeck/src/extension.ts:91 / extensions/vscode-taskdeck/src/extension.ts:94 - sendCapture() increments a generation at start and checks it only inside the delayed reset timer. Stale completion status updates are still unguarded: if capture A starts, capture B starts, and A finishes while B is still pending, A can set the status bar to $(check) Sent or $(error) Failed even though the newer capture is still in flight. That keeps the same overlapping-status class partially open, and can still hide the active capture's Sending... state until B completes. Expected fix: guard success/failure status updates with the same current-generation check, or model active/in-flight captures explicitly, and add an async regression test that proves an older completion cannot overwrite a newer capture's status.

Verified fixed

  • MEDIUM absolute-path leak: fixed. getSafeDocumentLabel() now preserves workspace-relative paths, reduces out-of-workspace local files to basename-only labels, and avoids local directory paths for untitled/non-file documents. context.test.ts covers the Windows absolute-path case.
  • LOW source label: fixed. sourceLabel() now maps VsCodeExtension and numeric 11 to VS Code, with coverage in inboxUtils.spec.ts.
  • LOW delayed status reset from #discussion_r3254712354: partially fixed. The delayed reset timer now checks statusResetGuard.isCurrent(statusGeneration) before returning to the idle status, and statusReset.test.ts covers stale generation rejection. The remaining finding above is the unguarded completion status before that timer.

Existing PR discussion reviewed

  • PR body, linked issue RFAI-11: Ambient channel hardening decision and prototype #983, issue comments, inline review comments, review submissions, and bot comments were inspected with GitHub MCP and gh.
  • Gemini quota and Copilot review-error comments contain no actionable code findings.
  • Prior findings for extension test runner, HTTP timeout/response handling, API URL validation, token trimming, stale voice callbacks, activation events, share-target propagation, final voice result after stop, absolute-path leakage, source labels, and delayed status resets were checked against current code/tests.

Commands / checks run

  • powershell -File scripts/check-git-env.ps1 - PASS.
  • git status --short --branch - inspected; local checkout is dirty on unrelated tst/1081-composable-coverage-part2, so review used remote refs only.
  • GitHub MCP _fetch_pr and _fetch_pr_comments - inspected PR metadata/diff/comments.
  • gh pr view 1079 --repo Chris0Jeky/Taskdeck --json number,title,headRefName,headRefOid,baseRefName,baseRefOid,author,state,isDraft,body,url,mergeStateStatus - confirmed head SHA and PR state.
  • gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/reviews --paginate, and gh pr view 1079 --repo Chris0Jeky/Taskdeck --comments - inspected comments/reviews/bot feedback.
  • git fetch origin main:refs/remotes/origin/main refs/pull/1079/head:refs/remotes/origin/pr/1079 - fetched current refs.
  • gh issue view 983 --repo Chris0Jeky/Taskdeck --comments --json number,title,state,body,comments,url,labels,author - inspected linked issue.
  • git diff --check origin/main...origin/pr/1079 - PASS.
  • git diff --name-status origin/main...origin/pr/1079, git diff --stat origin/main...origin/pr/1079, git diff --name-only origin/main...origin/pr/1079, git log --oneline --decorate --max-count=30 origin/main..origin/pr/1079, and targeted git diff --unified=... / git show origin/pr/1079:<path> inspections for the extension, voice composable, backend capture contracts, ADR, and inbox label changes.
  • git merge-base --is-ancestor origin/main origin/pr/1079 - PASS.
  • gh pr checks 1079 --repo Chris0Jeky/Taskdeck - completed checks were passing/skipping at snapshot; several required CI jobs were still pending, so the command exited non-zero.
  • One malformed local command, git diff --name-status --stat --check origin/main...origin/pr/1079, failed because those options cannot be combined; reran the checks separately as listed above.

Residual verification caveat: I did not run local build/test suites because this was a read-only review from a dirty checkout; this review is based on remote-ref source inspection plus current GitHub check status.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 20848e6191

ℹ️ 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".

"scripts": {
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"lint": "eslint src",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add ESLint dependency for the declared lint script

The extension manifest defines "lint": "eslint src", but this commit does not add eslint to devDependencies (or the lockfile), so npm run lint fails in a clean checkout with a command-not-found error. That makes the provided lint entrypoint non-functional for local verification and will break any CI job that invokes the lint script.

Useful? React with 👍 / 👎.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Fix evidence for re-review comment #1079 (comment) at head 1308419:

  • LOW stale completion status: fixed in 1308419 by applying the same generation guard to immediate success/failure status updates. Older captures can still report their toast/error message, but they cannot overwrite the status bar after a newer capture has started.
  • Extended status reset regression coverage to assert older completion generations are stale.

Verification after the fix:

  • extensions/vscode-taskdeck: npm test — PASS (context, apiUrl, manifest, status reset tests)
  • frontend/taskdeck-web: npx vitest --run src/tests/components/inbox/inboxUtils.spec.ts src/tests/composables/useVoiceCapture.spec.ts src/tests/public/shareTargetHandler.spec.ts src/tests/views/ShareTargetView.spec.ts src/tests/utils/captureQueue.spec.ts src/tests/composables/useCaptureQueueSync.spec.ts — PASS (64 tests)
  • frontend/taskdeck-web: npm run typecheck — PASS
  • frontend/taskdeck-web: npm run build — PASS

Fresh current-head review requested for 1308419 before merge.

Copy link
Copy Markdown
Owner Author

Fresh adversarial re-review - PR #1079 head 13084190356b69923024697e828c646722363a16

Verdict: not clean / not merge-ready. The absolute-path, source-label, delayed reset, and completion-status findings from comments #1079 (comment), #1079 (comment), and #1079 (comment) are fixed at the current pushed head. One later bot-review finding remains valid.

MEDIUM

  • extensions/vscode-taskdeck/package.json:68 - the extension declares "lint": "eslint src", but the extension package still has no eslint devDependency, no eslint lockfile entry, and no extension ESLint config; the repository root also has no package.json to provide an ancestor lint setup. In a clean checkout this declared verification entrypoint is not reproducible and will fail if a developer or CI job invokes it. This leaves the latest Codex inline finding unresolved: RFAI-11: Ambient channel hardening — VS Code extension + voice prototype #1079 (comment). Expected fix: either remove the lint script until there is a real extension lint lane, or add a complete extension lint setup (dependency, lockfile entry, and config) and verify npm run lint from extensions/vscode-taskdeck.

Verified fixed

  • MEDIUM absolute-path leak: fixed. getSafeDocumentLabel() now keeps workspace-relative labels, reduces out-of-workspace local files to basename-only labels, and avoids local directory paths for untitled/non-file documents. context.test.ts covers the Windows absolute-path case.
  • LOW source label: fixed. sourceLabel() maps VsCodeExtension and numeric 11 to VS Code, with coverage in inboxUtils.spec.ts.
  • LOW delayed status reset: fixed. sendCapture() now checks statusResetGuard.isCurrent(statusGeneration) before returning the status bar to idle, and statusReset.test.ts covers stale reset generations.
  • LOW completion status overwrite: fixed. sendCapture() now applies the same generation guard to immediate success and failure status updates, so older completions cannot overwrite a newer in-flight capture status.

Existing PR discussion reviewed

  • PR body, linked issue RFAI-11: Ambient channel hardening decision and prototype #983, issue comments, inline review comments, review submissions, and bot comments were inspected with GitHub MCP and gh.
  • Gemini quota and Copilot review-error comments contain no actionable code findings.
  • Earlier Codex/review findings for extension test runner, HTTP timeout/response handling, API URL validation, token trimming, stale voice callbacks, activation events, share-target propagation, final voice result after stop, absolute-path leakage, source labels, delayed reset, and completion status were checked against current code/tests.

Commands / checks run

  • powershell -File scripts/check-git-env.ps1 - PASS.
  • git status --short --branch - inspected; local checkout is dirty on unrelated tst/1081-composable-coverage-part2, so review used remote refs only.
  • GitHub MCP _fetch_pr and _fetch_pr_comments - inspected PR metadata/diff/comments.
  • gh pr view 1079 --repo Chris0Jeky/Taskdeck --json number,title,headRefName,headRefOid,baseRefName,baseRefOid,author,state,isDraft,body,url,mergeStateStatus,reviewDecision - confirmed current head and PR state.
  • gh api repos/Chris0Jeky/Taskdeck/issues/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/comments --paginate, gh api repos/Chris0Jeky/Taskdeck/pulls/1079/reviews --paginate, and gh pr view 1079 --repo Chris0Jeky/Taskdeck --comments - inspected all current comments/reviews/bot feedback available from the API.
  • git fetch origin main:refs/remotes/origin/main refs/pull/1079/head:refs/remotes/origin/pr/1079 - fetched current refs.
  • gh issue view 983 --repo Chris0Jeky/Taskdeck --comments --json number,title,state,body,comments,url,labels,author - inspected linked issue.
  • git merge-base --is-ancestor origin/main origin/pr/1079 - PASS.
  • git diff --check origin/main...origin/pr/1079 - PASS.
  • git diff --name-status origin/main...origin/pr/1079, git diff --stat origin/main...origin/pr/1079, git log --oneline --decorate --max-count=40 origin/main..origin/pr/1079, and targeted git show origin/pr/1079:<path> inspections for the extension manifest/scripts/lockfile, status reset handling, context/path labeling, inbox labels, voice composable, backend capture contracts, and ADR.
  • git grep -n 'eslint' origin/pr/1079 -- extensions/vscode-taskdeck and git ls-tree -r --name-only origin/pr/1079 extensions/vscode-taskdeck | Select-String -Pattern 'eslint|package|tsconfig' - confirmed only the lint script references ESLint and no extension ESLint dependency/config is present.
  • git show origin/pr/1079:package.json - failed because there is no repo-root package.json; this supports that no root-level package supplies the extension lint tooling.
  • gh pr checks 1079 --repo Chris0Jeky/Taskdeck - exited non-zero because several required jobs were still pending; completed checks shown were passing/skipping at snapshot.

Residual verification caveat: I did not run local build/test suites because this was a read-only review from a dirty checkout. This review is based on current remote-ref source inspection plus GitHub check status at the time of review.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1308419035

ℹ️ 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".

Comment on lines +81 to +85
fileName: 'C:\\Users\\alice\\Documents\\private-plan.ts',
uriScheme: 'file',
uriFsPath: 'C:\\Users\\alice\\Documents\\private-plan.ts',
});
assert(result === 'private-plan.ts', 'uses only basename for files outside a workspace');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make Windows-path assertion platform-independent

This test hard-codes a Windows-style path and expects getSafeDocumentLabel to return only private-plan.ts, but node:path.basename is platform-dependent: on Linux/macOS it treats backslashes as normal characters, so the value remains C:\\Users\\alice\\Documents\\private-plan.ts. In CI or local non-Windows runs, this causes npm test to fail even when the implementation is unchanged. Use a platform-agnostic expectation (or path.win32.basename for this fixture) so the extension test suite is runnable cross-platform.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pending

Development

Successfully merging this pull request may close these issues.

RFAI-11: Ambient channel hardening decision and prototype

2 participants