From ccd53914218d3cbab52e95ef9166899cf92c3197 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:46:43 +0800 Subject: [PATCH 1/9] feat(workflow): add risk-scoped productivity toolkit --- .agents/skills/clinical-change-proof/SKILL.md | 20 + .../clinical-change-proof/agents/openai.yaml | 4 + .agents/skills/database-flightplan/SKILL.md | 18 + .../database-flightplan/agents/openai.yaml | 4 + .agents/skills/live-design-sweep/SKILL.md | 17 + .../live-design-sweep/agents/openai.yaml | 4 + .agents/skills/operator-closeout/SKILL.md | 23 ++ .../operator-closeout/agents/openai.yaml | 4 + .agents/skills/rag-change-lab/SKILL.md | 16 + .../skills/rag-change-lab/agents/openai.yaml | 4 + .agents/skills/session-lifecycle/SKILL.md | 17 + .../session-lifecycle/agents/openai.yaml | 4 + .agents/skills/verify-triage-fix/SKILL.md | 21 + .../verify-triage-fix/agents/openai.yaml | 4 + .gitignore | 3 + AGENTS.md | 18 + docs/productivity-workflows.md | 27 ++ package.json | 21 +- scripts/ci-change-scope.mjs | 9 +- scripts/external-workflow.mjs | 64 ++++ scripts/productivity-core.mjs | 361 ++++++++++++++++++ scripts/productivity-workflow.mjs | 150 ++++++++ tests/productivity-workflow.test.ts | 88 +++++ 23 files changed, 893 insertions(+), 8 deletions(-) create mode 100644 .agents/skills/clinical-change-proof/SKILL.md create mode 100644 .agents/skills/clinical-change-proof/agents/openai.yaml create mode 100644 .agents/skills/database-flightplan/SKILL.md create mode 100644 .agents/skills/database-flightplan/agents/openai.yaml create mode 100644 .agents/skills/live-design-sweep/SKILL.md create mode 100644 .agents/skills/live-design-sweep/agents/openai.yaml create mode 100644 .agents/skills/operator-closeout/SKILL.md create mode 100644 .agents/skills/operator-closeout/agents/openai.yaml create mode 100644 .agents/skills/rag-change-lab/SKILL.md create mode 100644 .agents/skills/rag-change-lab/agents/openai.yaml create mode 100644 .agents/skills/session-lifecycle/SKILL.md create mode 100644 .agents/skills/session-lifecycle/agents/openai.yaml create mode 100644 .agents/skills/verify-triage-fix/SKILL.md create mode 100644 .agents/skills/verify-triage-fix/agents/openai.yaml create mode 100644 docs/productivity-workflows.md create mode 100644 scripts/external-workflow.mjs create mode 100644 scripts/productivity-core.mjs create mode 100644 scripts/productivity-workflow.mjs create mode 100644 tests/productivity-workflow.test.ts diff --git a/.agents/skills/clinical-change-proof/SKILL.md b/.agents/skills/clinical-change-proof/SKILL.md new file mode 100644 index 000000000..613f73335 --- /dev/null +++ b/.agents/skills/clinical-change-proof/SKILL.md @@ -0,0 +1,20 @@ +--- +name: clinical-change-proof +description: Build evidence for Database changes affecting clinical output, ingestion, retrieval, ranking, answers, citations, source rendering, privacy, owner scoping, document access, Supabase, or production behavior. Use for implementation, review, readiness, or handoff work that must satisfy clinical governance and conservative failure requirements. +--- + +# Clinical Change Proof + +1. Generate the scoped evidence plan: + `npm run workflow:clinical-proof -- --write-evidence` +2. Read only the relevant sections of `.github/pull_request_template.md`, `docs/clinical-governance.md`, and the domain runbook selected by the change. +3. Trace the full behavior boundary: input, authorization/owner scope, retrieval or write path, output contract, source evidence, logs, and failure fallback. +4. Prove locally that: + - private or service-role data remains server-only and fail-closed; + - source-backed claims and conservative unknown/outdated behavior are preserved; + - demo data remains distinct from clinical sources; + - rollback or feature-disable behavior is documented; + - affected behavior has focused regression coverage. +5. Run the local checks in the plan, narrowest first. The offline RAG contract is mandatory when retrieval or answer behavior is involved. +6. Present every live Supabase, OpenAI, hosted CI, or production-readiness command as a separate approval request. Never bundle approval implicitly into a local gate. +7. Complete the Clinical Governance Preflight and report evidence, gaps, rollback, and any SaMD implication. diff --git a/.agents/skills/clinical-change-proof/agents/openai.yaml b/.agents/skills/clinical-change-proof/agents/openai.yaml new file mode 100644 index 000000000..b526d3734 --- /dev/null +++ b/.agents/skills/clinical-change-proof/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Clinical Change Proof" + short_description: "Build clinical governance evidence" + default_prompt: "Use $clinical-change-proof to assess this clinical change and produce the required local evidence, governance checks, and provider approval gates." diff --git a/.agents/skills/database-flightplan/SKILL.md b/.agents/skills/database-flightplan/SKILL.md new file mode 100644 index 000000000..8fd01a935 --- /dev/null +++ b/.agents/skills/database-flightplan/SKILL.md @@ -0,0 +1,18 @@ +--- +name: database-flightplan +description: Plan safe, risk-scoped work in the Database repository by inspecting the current diff, selecting the smallest local verification ladder, and separating provider-backed checks into explicit approval gates. Use before non-trivial source, config, test, UI, database, retrieval, clinical, dependency, CI, or deployment changes, or when the user asks what checks a change needs. +--- + +# Database Flightplan + +1. Run the repository task-start preflight if it has not run for the current task. Inspect branch and full Git status; preserve unrelated work. +2. Generate the plan: + `npm run workflow:flightplan -- --write-evidence` + Use `--files pathA,pathB` when planning proposed paths before editing. +3. Confirm the risk classes match the actual behavior, not only filenames. Add a focused check when the change crosses an unclassified boundary. +4. Execute the narrowest relevant check first. Use `--run` only when running the full printed local/offline sequence is proportionate. +5. Never execute anything listed under `approvalRequired` without explicit user confirmation. Treat indirect provider calls the same way. +6. After fixes, run `npm run verify:pr-local` when the change is ready for handoff. Add UI or domain gates only when the plan selects them. +7. Report changed files, checks and results, checks not run, approval gates, branch state, and residual risk. + +Keep the planner authoritative for deterministic selection. Do not reproduce its path tables in the skill. diff --git a/.agents/skills/database-flightplan/agents/openai.yaml b/.agents/skills/database-flightplan/agents/openai.yaml new file mode 100644 index 000000000..f2f201b90 --- /dev/null +++ b/.agents/skills/database-flightplan/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Database Flightplan" + short_description: "Plan safe repo changes and verification" + default_prompt: "Use $database-flightplan to inspect the current Database repo change and produce a risk-scoped local verification and approval plan." diff --git a/.agents/skills/live-design-sweep/SKILL.md b/.agents/skills/live-design-sweep/SKILL.md new file mode 100644 index 000000000..40dfe469c --- /dev/null +++ b/.agents/skills/live-design-sweep/SKILL.md @@ -0,0 +1,17 @@ +--- +name: live-design-sweep +description: Inspect and improve the running Database application across routes, responsive breakpoints, keyboard interaction, accessibility modes, and shared UI surfaces. Use for design reviews, UI polish, browser QA, screenshots, mobile checks, accessibility work, or requests to find and fix visual or interaction defects across the app. +--- + +# Live Design Sweep + +1. Run `npm run workflow:design-sweep` to print the proof contract. +2. Run `npm run ensure` before opening the app. Use only the identity-verified URL it prints. +3. Derive route coverage from `docs/site-map.md` and changed navigation. Inspect representative content-rich, empty, loading, error, dialog, and long-scroll states. +4. Cover 320, 390, 639, 768, 1440, and 1920 px where applicable. Check horizontal overflow, single-scrollport ownership, sticky/fixed collisions, tap targets, wrapping, and safe-area behavior. +5. Check keyboard order, visible focus, dialog focus trapping and restoration, accessible names, contrast, reduced motion, forced colors, zoom, and screen-reader semantics. +6. Record reproducible defects with route, viewport, trigger, expected behavior, and screenshot or test evidence. Fix shared primitives before duplicating route-specific patches. +7. Add the smallest regression proof, then run focused Playwright, accessibility, `verify:ui`, and `verify:cheap` in that order as warranted. +8. Write durable route/breakpoint coverage to the task report or `docs/archive/design-qa.md` when the pass is broad. + +Do not claim external design fidelity unless a target design or screenshot was supplied. diff --git a/.agents/skills/live-design-sweep/agents/openai.yaml b/.agents/skills/live-design-sweep/agents/openai.yaml new file mode 100644 index 000000000..6202e84bf --- /dev/null +++ b/.agents/skills/live-design-sweep/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Live Design Sweep" + short_description: "Inspect and polish every live route" + default_prompt: "Use $live-design-sweep to inspect the running Database app across routes and breakpoints, fix confirmed UI issues, and verify accessibility." diff --git a/.agents/skills/operator-closeout/SKILL.md b/.agents/skills/operator-closeout/SKILL.md new file mode 100644 index 000000000..30f81466c --- /dev/null +++ b/.agents/skills/operator-closeout/SKILL.md @@ -0,0 +1,23 @@ +--- +name: operator-closeout +description: Convert pending Database operator, deployment, migration, secret, live-service, and governance debt into a current, deduplicated, dependency-ordered, approval-gated execution batch with pre-state, post-state, rollback, and evidence requirements. Use when closing operator backlog items, preparing launch actions, or reconciling confirmation-required follow-ups. +--- + +# Operator Closeout + +1. Inventory candidates with: + `npm run workflow:operator-closeout -- --write-evidence` +2. Treat the output as discovery, not truth. Verify each candidate against its linked runbook and current local state; remove completed, superseded, duplicate, or stale items. +3. Group remaining actions by provider and blast radius. Order prerequisites, read-only probes, reversible writes, irreversible writes, and post-verification. +4. For every action record: + - exact target and environment; + - external effect and credentials required; + - pre-state query or evidence; + - exact command or dashboard action; + - success condition and post-state proof; + - rollback or recovery path; + - expected cost or downtime. +5. Ask for explicit approval for the concrete batch. Approval for one provider or command does not authorize another. +6. Execute approved actions one at a time, stop on unexpected state, and update durable operator documentation only with observed evidence. + +The planner is intentionally plan-only. Never add automatic provider execution to it. diff --git a/.agents/skills/operator-closeout/agents/openai.yaml b/.agents/skills/operator-closeout/agents/openai.yaml new file mode 100644 index 000000000..90a997884 --- /dev/null +++ b/.agents/skills/operator-closeout/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Operator Closeout" + short_description: "Turn operator debt into safe batches" + default_prompt: "Use $operator-closeout to inventory pending operator actions, deduplicate them, and prepare a safe approval-gated execution batch." diff --git a/.agents/skills/rag-change-lab/SKILL.md b/.agents/skills/rag-change-lab/SKILL.md new file mode 100644 index 000000000..c25003a1e --- /dev/null +++ b/.agents/skills/rag-change-lab/SKILL.md @@ -0,0 +1,16 @@ +--- +name: rag-change-lab +description: Validate Database retrieval, ranking, chunking, query classification, source selection, citations, answer synthesis, grounding, and RAG privacy changes with focused tests and provider-free golden evaluation before requesting live evaluation approval. Use for RAG implementation, refactoring, debugging, regression analysis, or merge-readiness work. +--- + +# RAG Change Lab + +1. Generate the lab plan: + `npm run workflow:rag-lab -- --write-evidence` +2. Read `docs/retrieval-quality-runbook.md` and only the change-relevant RAG architecture or threat-model sections. +3. Define the invariant before editing: retrieval recall/ranking, owner scope, grounding, citation correctness, unsupported-query behavior, latency, cache invalidation, or prompt-injection resistance. +4. Run the closest unit/property tests first, followed by `npm run eval:rag:offline`. Inspect cases and metrics rather than relying only on exit status. +5. Make minimal changes and compare against the accepted baseline. Treat recall, MRR, grounded support, citation failure, numeric grounding, source governance, and strategy mix as separate signals. +6. Run `verify:cheap` after focused proof. Use `verify:pr-local` for handoff. +7. Stop before `eval:retrieval:quality`, `eval:rag`, `eval:quality`, or any live corpus/provider operation. Present the exact command, expected cost/effect, required environment, baseline, and success threshold for explicit approval. +8. Report whether remaining uncertainty is code, corpus, owner configuration, live-provider variance, or environment. diff --git a/.agents/skills/rag-change-lab/agents/openai.yaml b/.agents/skills/rag-change-lab/agents/openai.yaml new file mode 100644 index 000000000..dd40ad7a9 --- /dev/null +++ b/.agents/skills/rag-change-lab/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "RAG Change Lab" + short_description: "Validate retrieval and answer changes" + default_prompt: "Use $rag-change-lab to select and run the safe offline checks for this RAG change and prepare any live evaluation approval request." diff --git a/.agents/skills/session-lifecycle/SKILL.md b/.agents/skills/session-lifecycle/SKILL.md new file mode 100644 index 000000000..2366083dc --- /dev/null +++ b/.agents/skills/session-lifecycle/SKILL.md @@ -0,0 +1,17 @@ +--- +name: session-lifecycle +description: Manage Database repository work safely from task start through verification, handoff, merge proof, and conservative worktree or branch cleanup. Use when starting a task, checking session state, preparing completed work for handoff, confirming a squash merge landed, or cleaning up only proven-redundant task state. +--- + +# Session Lifecycle + +1. Select the phase and generate its plan: + `npm run workflow:lifecycle -- --phase ` +2. At task start, run the mandatory `start-codex-task.ps1` preflight, read applicable `AGENTS.md`, and preserve all existing work. Do not use the shared stash across worktrees. +3. During work, recheck branch and status before edits and before handoff. Use `database-flightplan` for non-trivial change verification. +4. At handoff, stage explicit coherent paths only, verify locally, and inspect the staged diff. Commit, push, PR creation, hosted CI, and merge remain authorization-bound. +5. After a squash merge, verify the reviewed content against fetched `origin/main`; do not rely on ancestry alone. Check for late orphaned commits before cleanup. +6. Remove a worktree or branch only after exact path/ref resolution, clean-state proof, and content-equivalence evidence. Preserve dirty, active, ambiguous, open-PR, or patch-unique work. +7. Record the outcome in `docs/branch-review-ledger.md` when the repo review protocol requires it. + +The older `.claude/skills/newtask`, `handoff`, and `prlanded` remain compatibility surfaces. Use this skill as the agent-neutral orchestration contract. diff --git a/.agents/skills/session-lifecycle/agents/openai.yaml b/.agents/skills/session-lifecycle/agents/openai.yaml new file mode 100644 index 000000000..ff66a9a4f --- /dev/null +++ b/.agents/skills/session-lifecycle/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Session Lifecycle" + short_description: "Manage safe task start through cleanup" + default_prompt: "Use $session-lifecycle to inspect and advance this Database task through start, verification, handoff, merge proof, or safe cleanup." diff --git a/.agents/skills/verify-triage-fix/SKILL.md b/.agents/skills/verify-triage-fix/SKILL.md new file mode 100644 index 000000000..670f26004 --- /dev/null +++ b/.agents/skills/verify-triage-fix/SKILL.md @@ -0,0 +1,21 @@ +--- +name: verify-triage-fix +description: Diagnose and fix local Database repository verification failures using the smallest reproducible target, failure classification, known-flake evidence, and narrow reruns before broader gates. Use when lint, typecheck, Vitest, Playwright, build, offline RAG, workflow tooling, or another local check fails or appears hung. +--- + +# Verify Triage Fix + +1. Capture the exact failed command, exit code, complete actionable error, and relevant artifact path. Do not combine more checks until the failure is understood. +2. Classify a saved log with: + `npm run workflow:triage -- --log ` + Without `--log`, the planner uses `.local/workflow-last-failure.json` when present. +3. Verify the classification: + - regression: reproduce the smallest test, typecheck target, or build surface; + - environment: inspect runtime, paths, dependencies, process ownership, and timeouts; + - provider/configuration: stop and request approval or missing configuration; + - known flake: prove the signature matches `tests/flake-ledger.json` before treating it as such. +4. Change only the smallest confirmed cause. Do not hide failures by weakening assertions, increasing timeouts blindly, or broad refactoring. +5. Rerun the smallest failing target after each fix. Widen to `verify:cheap`, `verify:ui`, or `verify:pr-local` only after it passes. +6. Distinguish code defects, pre-existing failures, environment blockers, and provider gates in the final report. + +Never use provider access to diagnose a local failure without explicit confirmation. diff --git a/.agents/skills/verify-triage-fix/agents/openai.yaml b/.agents/skills/verify-triage-fix/agents/openai.yaml new file mode 100644 index 000000000..20ea968cd --- /dev/null +++ b/.agents/skills/verify-triage-fix/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Verify Triage Fix" + short_description: "Diagnose and fix verification failures" + default_prompt: "Use $verify-triage-fix to diagnose the current local verification failure, fix the smallest confirmed cause, and rerun focused checks." diff --git a/.gitignore b/.gitignore index 66f2dcf3b..b17f695ae 100644 --- a/.gitignore +++ b/.gitignore @@ -18,11 +18,14 @@ /playwright-cli/ /.playwright-cli/ /playwright/.auth/ +/.local/ /.codex/ /.agents/* !/.agents/plugins/ !/.agents/plugins/marketplace.json !/.agents/plugins/api_marketplace.json +!/.agents/skills/ +!/.agents/skills/** /skills-lock.json # next.js diff --git a/AGENTS.md b/AGENTS.md index d5d498c51..c834b0539 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -317,6 +317,24 @@ After completing `upload`, summarize the current branch and worktree state, whet + + +## Repository productivity skills + +Use the repo-local skills under `.agents/skills/` when their descriptions match. They are thin orchestration layers over `scripts/productivity-workflow.mjs`; do not copy their procedures into this file. + +- `database-flightplan`: plan risk-scoped verification before non-trivial changes. +- `verify-triage-fix`: diagnose and repair local verification failures with the smallest reproducer. +- `clinical-change-proof`: assemble clinical, privacy, source, and rollback evidence. +- `live-design-sweep`: inspect the running app across routes, breakpoints, and accessibility modes. +- `rag-change-lab`: validate retrieval and answer changes offline first, then prepare live-eval approval gates. +- `operator-closeout`: turn pending operator debt into a deduplicated, approval-gated batch. +- `session-lifecycle`: manage safe start, handoff, merge proof, and cleanup transitions. + +Run the matching planner command in `docs/productivity-workflows.md` without side effects by default. Add `-- --run` only to execute its local/offline checks. The workflow engine must never execute commands listed under `approvalRequired`. + + + ## Codex GitHub review behavior These instructions apply to Codex GitHub pull request reviews and Codex tasks started from PR comments. diff --git a/docs/productivity-workflows.md b/docs/productivity-workflows.md new file mode 100644 index 000000000..112a66e8e --- /dev/null +++ b/docs/productivity-workflows.md @@ -0,0 +1,27 @@ +# Productivity workflows + +The repository exposes seven offline-first workflow planners. Each planner inspects the current change through `scripts/ci-change-scope.mjs`, prints a minimal local verification sequence, and separates provider-backed commands into an explicit approval section. + +| Command | Purpose | +| ----------------------------------------------- | -------------------------------------------------------------------------------------- | +| `npm run workflow:flightplan` | Classify the current diff and select the smallest appropriate verification ladder. | +| `npm run workflow:triage` | Classify a saved or recent workflow failure before attempting a fix. | +| `npm run workflow:clinical-proof` | Produce clinical-governance, privacy, source, rollback, and verification requirements. | +| `npm run workflow:design-sweep` | Plan the live route, breakpoint, accessibility, and Chromium sweep. | +| `npm run workflow:rag-lab` | Select focused retrieval tests, offline RAG evaluation, and gated live evaluations. | +| `npm run workflow:operator-closeout` | Inventory and deduplicate pending operator or confirmation-required actions. | +| `npm run workflow:lifecycle -- --phase ` | Plan `status`, `start`, `handoff`, `landed`, or `cleanup` lifecycle work. | + +## Safe execution + +- Planning is read-only by default. +- Add `-- --run` to run only the printed local/offline checks. +- Provider-backed commands are never executed by the planner, even with `--run`. +- Add `-- --write-evidence` to save structured evidence under ignored `.local/workflow-evidence/`. +- Add `-- --json` for machine-readable output. +- Use `-- --files pathA,pathB` to plan an explicit proposed change before editing. +- Use `workflow:triage -- --log ` to classify a captured failure. + +The existing shared `workflow:run`, `workflow:status`, `workflow:verify`, `workflow:deps`, `workflow:clean-state`, `workflow:export`, and `workflow:handoff` commands now resolve their shared implementation through the repository's Git common directory. This keeps them portable in linked and detached Codex worktrees. Set `CODEX_LOCAL_WORKFLOW_ROOT` only when the shared tools live somewhere non-standard. + +The matching agent skills live in `.agents/skills/`. Skills provide judgment and repair loops; scripts provide deterministic selection, safety enforcement, and evidence. diff --git a/package.json b/package.json index 759130ccd..df1c29c3d 100644 --- a/package.json +++ b/package.json @@ -145,13 +145,20 @@ "audit:tables": "node scripts/run-tsx.mjs scripts/audit-tables.ts", "samples": "node scripts/run-tsx.mjs scripts/generate-sample-documents.ts", "samples:check": "node scripts/run-tsx.mjs scripts/check-sample-extraction.ts", - "workflow:run": "node ../.local-dev/workflow-run.mjs", - "workflow:status": "node ../.local-dev/workflow-status.mjs", - "workflow:verify": "node ../.local-dev/workflow-verify.mjs", - "workflow:deps": "node ../.local-dev/workflow-deps.mjs", - "workflow:clean-state": "node ../.local-dev/workflow-clean-state.mjs", - "workflow:export": "node ../.local-dev/workflow-export.mjs", - "workflow:handoff": "node ../.local-dev/workflow-handoff.mjs", + "workflow:run": "node scripts/external-workflow.mjs run", + "workflow:status": "node scripts/external-workflow.mjs status", + "workflow:verify": "node scripts/external-workflow.mjs verify", + "workflow:deps": "node scripts/external-workflow.mjs deps", + "workflow:clean-state": "node scripts/external-workflow.mjs clean-state", + "workflow:export": "node scripts/external-workflow.mjs export", + "workflow:handoff": "node scripts/external-workflow.mjs handoff", + "workflow:flightplan": "node scripts/productivity-workflow.mjs flightplan", + "workflow:triage": "node scripts/productivity-workflow.mjs triage", + "workflow:clinical-proof": "node scripts/productivity-workflow.mjs clinical-proof", + "workflow:design-sweep": "node scripts/productivity-workflow.mjs design-sweep", + "workflow:rag-lab": "node scripts/productivity-workflow.mjs rag-lab", + "workflow:operator-closeout": "node scripts/productivity-workflow.mjs operator-closeout", + "workflow:lifecycle": "node scripts/productivity-workflow.mjs lifecycle", "check:drift": "node scripts/run-tsx.mjs scripts/check-drift.ts", "drift:manifest": "node scripts/run-tsx.mjs scripts/generate-drift-manifest.ts" }, diff --git a/scripts/ci-change-scope.mjs b/scripts/ci-change-scope.mjs index b5738a34b..e5eb96505 100644 --- a/scripts/ci-change-scope.mjs +++ b/scripts/ci-change-scope.mjs @@ -68,11 +68,12 @@ const docPatterns = [ const workflowPatterns = [ ".github/workflows", ".github/actions", + ".agents/skills", ".github/pull_request_template.md", "AGENTS.md", "docs/codex-review-protocol.md", "docs/process-hardening.md", - /^scripts\/(?:ci-change-scope|verify-pr-local|eval-rag-offline|check-github-action-pins|check-codex-autofix-workflow)\.mjs$/, + /^scripts\/(?:ci-change-scope|verify-pr-local|eval-rag-offline|check-github-action-pins|check-codex-autofix-workflow|productivity-core|productivity-workflow|external-workflow)\.mjs$/, ]; const codexAutofixPatterns = [ @@ -462,6 +463,12 @@ function selfTest() { docs_only: false, build_changed: false, }); + assertScope("repo-skill", [".agents/skills/database-flightplan/SKILL.md", "scripts/productivity-core.mjs"], { + workflow_changed: true, + source_changed: true, + docs_only: false, + build_changed: false, + }); assertScope( "codex-autofix", [".github/workflows/codex-autofix-review-comments.yml", "AGENTS.md", "scripts/check-codex-autofix-workflow.mjs"], diff --git a/scripts/external-workflow.mjs b/scripts/external-workflow.mjs new file mode 100644 index 000000000..f1b384f07 --- /dev/null +++ b/scripts/external-workflow.mjs @@ -0,0 +1,64 @@ +#!/usr/bin/env node +import { execFileSync, spawnSync } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const allowed = new Set(["run", "status", "verify", "deps", "clean-state", "export", "handoff"]); + +export function workflowRootCandidates(cwd = process.cwd(), gitCommonDir = "", env = process.env) { + const candidates = []; + if (env.CODEX_LOCAL_WORKFLOW_ROOT) candidates.push(path.resolve(env.CODEX_LOCAL_WORKFLOW_ROOT)); + candidates.push(path.resolve(cwd, "..", ".local-dev")); + if (gitCommonDir) { + const common = path.isAbsolute(gitCommonDir) ? gitCommonDir : path.resolve(cwd, gitCommonDir); + candidates.push(path.join(path.dirname(path.dirname(common)), ".local-dev")); + } + return [...new Set(candidates.map((candidate) => path.normalize(candidate)))]; +} + +export function resolveExternalWorkflow(name, cwd = process.cwd(), gitCommonDir = "", env = process.env) { + if (!allowed.has(name)) throw new Error(`Unknown external workflow: ${name}`); + const filename = `workflow-${name}.mjs`; + for (const root of workflowRootCandidates(cwd, gitCommonDir, env)) { + const candidate = path.join(root, filename); + if (fs.existsSync(candidate)) return candidate; + } + throw new Error( + `Could not locate ${filename}. Set CODEX_LOCAL_WORKFLOW_ROOT or install the shared .local-dev workflows.`, + ); +} + +function main() { + const [name, ...args] = process.argv.slice(2); + if (!allowed.has(name)) { + console.error(`Usage: node scripts/external-workflow.mjs <${[...allowed].join("|")}> [args]`); + process.exit(1); + } + let gitCommonDir = ""; + try { + gitCommonDir = execFileSync("git", ["rev-parse", "--git-common-dir"], { + cwd: process.cwd(), + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }).trim(); + } catch { + // The direct parent candidate and explicit environment override still work outside Git. + } + const target = resolveExternalWorkflow(name, process.cwd(), gitCommonDir); + const result = spawnSync(process.execPath, [target, ...args], { + cwd: process.cwd(), + stdio: "inherit", + windowsHide: true, + }); + process.exit(result.status ?? 1); +} + +if (path.resolve(process.argv[1] || "") === path.resolve(fileURLToPath(import.meta.url))) { + try { + main(); + } catch (error) { + console.error(`[external-workflow] ${error instanceof Error ? error.message : String(error)}`); + process.exit(1); + } +} diff --git a/scripts/productivity-core.mjs b/scripts/productivity-core.mjs new file mode 100644 index 000000000..872bf5602 --- /dev/null +++ b/scripts/productivity-core.mjs @@ -0,0 +1,361 @@ +import { execFileSync, spawnSync } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; + +const PROVIDER_COMMAND_PATTERN = + /(?:check:supabase-project|check:production-readiness|verify:release(?:\s|$)|eval:retrieval:quality|eval:rag(?:\s|$)|eval:quality(?:\s|$)|gh\s|railway\s|supabase\s)/i; + +const RISK_PATTERNS = { + ui: [ + /^src\/app\/(?!api\/)/, + "src/components", + "src/styles", + "public", + /^tests\/ui-.*\.spec\.ts$/, + /^playwright.*\.ts$/, + ], + database: ["supabase", "src/lib/supabase", /^src\/app\/api\/(?:documents|images|ingestion|jobs|search|upload)/], + retrieval: [ + /^src\/lib\/(?:rag|retrieval|ranking|clinical-search|smart-rag|citations|evidence|source|chunking)/, + /^src\/app\/api\/(?:answer|search)/, + /^scripts\/(?:eval-|compare-retrieval|retrieval-|profile-retrieval|tune-search)/, + /^tests\/(?:rag|retrieval|answer|citation|evidence|source|clinical-safety)/, + ], + clinical: [ + /^src\/app\/api\/(?:answer|differentials|documents|ingestion|medications|registry|search)/, + /^src\/lib\/(?:rag|retrieval|clinical|answer|citation|evidence|source|owner-scope|query-privacy)/, + /^supabase\//, + /^docs\/(?:clinical-governance|privacy-impact|samd-|source-governance)/, + ], + privacy: [ + /(?:auth|permission|privacy|private|owner-scope|query-privacy|rls|security)/i, + /^src\/app\/api\/(?:documents|images|upload)/, + ], + deployment: [ + "Dockerfile", + "Dockerfile.worker", + "railway.app.json", + "railway.worker.json", + ".github/workflows", + "next.config.ts", + ], + dependency: ["package.json", "package-lock.json", ".npmrc"], + workflow: [ + "AGENTS.md", + ".agents/skills", + ".claude/skills", + "scripts/productivity-core.mjs", + "scripts/productivity-workflow.mjs", + "scripts/external-workflow.mjs", + ], +}; + +function normalizePath(value) { + return String(value || "") + .replaceAll("\\", "/") + .replace(/^\.\//, ""); +} + +function matches(file, pattern) { + if (typeof pattern === "string") return file === pattern || file.startsWith(`${pattern}/`); + return pattern.test(file); +} + +function uniqueBy(items, key) { + const seen = new Set(); + return items.filter((item) => { + const value = item[key]; + if (seen.has(value)) return false; + seen.add(value); + return true; + }); +} + +function check(command, reason) { + return { command, reason }; +} + +export function normalizeFiles(files = []) { + return [...new Set(files.map(normalizePath).filter(Boolean))].sort(); +} + +export function classifyRisks(files = []) { + const normalized = normalizeFiles(files); + const risks = Object.fromEntries( + Object.entries(RISK_PATTERNS).map(([name, patterns]) => [ + name, + normalized.some((file) => patterns.some((pattern) => matches(file, pattern))), + ]), + ); + risks.docsOnly = normalized.length > 0 && normalized.every((file) => /(?:^docs\/|\.mdx?$)/i.test(file)); + return risks; +} + +function baseLocalChecks(risks) { + if (risks.docsOnly) { + return [ + check("npm run format:check", "Confirm repository formatting."), + check("npm run docs:check-links", "Validate documentation links."), + check("npm run docs:check-scripts", "Validate documented package commands."), + ]; + } + + const checks = [check("npm run verify:cheap", "Run the fast offline repository gate.")]; + if (risks.retrieval || risks.clinical) { + checks.unshift( + check("npm run eval:rag:offline", "Protect retrieval and answer contracts without provider access."), + ); + } + if (risks.ui) { + checks.push(check("npm run ensure", "Start or verify the identity-checked local app before browser work.")); + checks.push(check("npm run test:e2e:critical", "Exercise the smallest high-value Chromium path.")); + checks.push(check("npm run verify:ui", "Run the full local Chromium UI gate after focused fixes.")); + } + checks.push(check("npm run verify:pr-local", "Mirror the risk-scoped PR gate before handoff.")); + return checks; +} + +function approvalChecks(risks) { + const approvals = []; + if (risks.database) { + approvals.push( + check("npm run check:supabase-project", "Confirm the live Supabase target after explicit approval."), + ); + } + if (risks.retrieval) { + approvals.push( + check( + "npm run eval:retrieval:quality", + "Run the live 36-case golden retrieval evaluation after explicit approval.", + ), + ); + } + if (risks.clinical) { + approvals.push( + check( + "npm run check:production-readiness", + "Run the production/provider readiness gate after explicit approval.", + ), + ); + } + if (risks.retrieval && risks.clinical) { + approvals.push(check("npm run eval:rag -- --limit 15", "Sample live answer generation after explicit approval.")); + approvals.push( + check("npm run eval:quality -- --rag-only", "Check live grounded-answer invariants after explicit approval."), + ); + } + if (risks.deployment) { + approvals.push( + check("npm run verify:release", "Run the complete release gate only after confirming its provider effects."), + ); + } + return approvals; +} + +function workflowSummary(workflow) { + return { + flightplan: "Risk-scoped change and verification plan", + triage: "Focused failure diagnosis and repair loop", + "clinical-proof": "Clinical governance and evidence plan", + "design-sweep": "Live route, responsive, and accessibility sweep", + "rag-lab": "Offline-first retrieval and answer validation lab", + "operator-closeout": "Approval-gated operator backlog batch", + lifecycle: "Safe task start, handoff, merge-proof, and cleanup lifecycle", + }[workflow]; +} + +export function buildWorkflowPlan(workflow, files = [], options = {}) { + const normalized = normalizeFiles(files); + const risks = classifyRisks(normalized); + let localChecks = baseLocalChecks(risks); + let approvalRequired = approvalChecks(risks); + const proof = ["Record exact commands and exit codes.", "Preserve unrelated staged, unstaged, and untracked work."]; + + if (workflow === "triage") { + localChecks = []; + proof.push( + "Reproduce the smallest failing target before changing code.", + "Rerun that target after each fix before widening verification.", + ); + } else if (workflow === "clinical-proof") { + localChecks = uniqueBy( + [check("npm run eval:rag:offline", "Prove the code-backed offline clinical contract."), ...localChecks], + "command", + ); + proof.push( + "Complete the Clinical Governance Preflight in .github/pull_request_template.md.", + "Document source, privacy, owner-scope, rollback, and SaMD implications.", + ); + } else if (workflow === "design-sweep") { + localChecks = [ + check("npm run ensure", "Start the verified app and use the printed URL."), + check("npm run test:e2e:critical", "Prove primary flows before the broad sweep."), + check("npm run test:e2e:accessibility", "Run the focused accessibility gate."), + check("npm run verify:ui", "Run the full local Chromium matrix."), + check("npm run verify:cheap", "Verify source changes after UI fixes."), + ]; + approvalRequired = []; + proof.push( + "Cover 320, 390, 639, 768, 1440, and 1920 px.", + "Check overflow, scroll ownership, keyboard use, focus, reduced motion, and forced colors.", + ); + } else if (workflow === "rag-lab") { + localChecks = [ + check( + "npm run test -- tests/retrieval-selection.test.ts tests/rag-routing.test.ts tests/rag-offline-contract.test.ts", + "Run focused retrieval and answer contract tests.", + ), + check("npm run eval:rag:offline", "Run the provider-free golden RAG preflight."), + check("npm run verify:cheap", "Catch cross-cutting static and unit regressions."), + ]; + approvalRequired = uniqueBy( + [ + check("npm run eval:retrieval:quality", "Require 36/36 against the live corpus after explicit approval."), + check("npm run eval:rag -- --limit 15", "Sample live answer generation after explicit approval."), + check("npm run eval:quality -- --rag-only", "Compare grounded-answer invariants after explicit approval."), + ], + "command", + ); + proof.push( + "Compare recall, MRR, grounded support, citation failure, and numeric-grounding failure with the accepted baseline.", + ); + } else if (workflow === "operator-closeout") { + localChecks = []; + approvalRequired = []; + proof.push( + "Deduplicate stale or already-completed actions before requesting approval.", + "Capture pre-state, command, post-state, and rollback evidence for every approved action.", + ); + } else if (workflow === "lifecycle") { + const phase = options.phase || "status"; + if (!new Set(["status", "start", "handoff", "landed", "cleanup"]).has(phase)) { + throw new Error(`Unknown lifecycle phase: ${phase}`); + } + localChecks = phase === "handoff" ? [check("npm run verify:pr-local", "Complete the local handoff gate.")] : []; + approvalRequired = + phase === "handoff" + ? [ + check("git push -u origin ", "Publishing requires explicit user authorization."), + check("gh pr create --base main", "GitHub interaction requires explicit user authorization."), + ] + : phase === "landed" + ? [ + check( + "gh pr view --json state,mergeCommit,mergedAt", + "GitHub interaction requires explicit user authorization.", + ), + ] + : []; + proof.push( + "Verify branch and worktree state at every transition.", + "Use content equality for squash-merge proof before cleanup.", + ); + } + + if (localChecks.some((item) => PROVIDER_COMMAND_PATTERN.test(item.command))) { + throw new Error(`Unsafe workflow definition: ${workflow} placed a provider-backed command in localChecks.`); + } + + return { + workflow, + summary: workflowSummary(workflow), + files: normalized, + risks, + localChecks: uniqueBy(localChecks, "command"), + approvalRequired: uniqueBy(approvalRequired, "command"), + proof, + }; +} + +export function analyzeFailureText(text = "", knownFlakes = []) { + const value = String(text); + const lower = value.toLowerCase(); + const knownFlake = knownFlakes.find((entry) => entry.pattern && lower.includes(String(entry.pattern).toLowerCase())); + if (knownFlake) return { category: "known-flake", confidence: "high", reason: knownFlake.id || knownFlake.pattern }; + if (/module_not_found|cannot find module|enoent|not recognized as an internal|command not found/.test(lower)) { + return { category: "environment", confidence: "high", reason: "Missing executable, module, or path." }; + } + if ( + /missing.*(?:api[_ -]?key|secret|credential)|(?:api[_ -]?key|secret|credential)\s+(?:is\s+)?missing|unauthorized|forbidden|quota|rate limit|supabase project/.test( + lower, + ) + ) { + return { + category: "provider-or-configuration", + confidence: "high", + reason: "Credentials, authorization, quota, or live-provider configuration.", + }; + } + if (/timed? out|timeout|etimedout|browser has been closed|worker.*exited/.test(lower)) { + return { + category: "environment-or-timeout", + confidence: "medium", + reason: "Timeout or runtime process failure; inspect active processes and artifacts.", + }; + } + if (/assertionerror|expected .* received|tests? failed|type error|eslint.*error|syntaxerror/.test(lower)) { + return { + category: "probable-regression", + confidence: "medium", + reason: "A deterministic code or test assertion failed.", + }; + } + return { + category: "unclassified", + confidence: "low", + reason: "Reproduce the smallest target and capture its complete error output.", + }; +} + +export function extractOperatorItemsFromText(text, source = "unknown") { + return String(text) + .split(/\r?\n/) + .map((line, index) => ({ source, line: index + 1, text: line.trim() })) + .filter(({ text: line }) => + /(?:⏳\s*pending|operator[- ]only|operator follow-up|confirmation-required|remaining operator items)/i.test(line), + ) + .filter(({ text: line }) => !/^#/.test(line)); +} + +export function scanOperatorBacklog(repoRoot = process.cwd()) { + const sources = ["docs/operator-backlog.md", "docs/process-hardening.md"]; + const items = sources.flatMap((source) => { + const absolute = path.join(repoRoot, source); + return fs.existsSync(absolute) ? extractOperatorItemsFromText(fs.readFileSync(absolute, "utf8"), source) : []; + }); + return uniqueBy(items, "text"); +} + +export function readChangeScope(files, repoRoot = process.cwd()) { + const args = ["scripts/ci-change-scope.mjs", "--json"]; + if (files?.length) args.push("--files", normalizeFiles(files).join(",")); + const output = execFileSync(process.execPath, args, { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "pipe", "inherit"], + }); + return JSON.parse(output); +} + +export function runLocalChecks(checks, repoRoot = process.cwd()) { + for (const item of checks) { + console.log(`\n[workflow] ${item.command}`); + const result = + process.platform === "win32" + ? spawnSync("cmd.exe", ["/d", "/s", "/c", item.command], { cwd: repoRoot, stdio: "inherit", windowsHide: true }) + : spawnSync("sh", ["-lc", item.command], { cwd: repoRoot, stdio: "inherit" }); + if (result.status !== 0) return { status: result.status ?? 1, failed: item.command }; + } + return { status: 0 }; +} + +export function writeWorkflowEvidence(plan, repoRoot = process.cwd()) { + const directory = path.join(repoRoot, ".local", "workflow-evidence"); + fs.mkdirSync(directory, { recursive: true }); + const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); + const target = path.join(directory, `${timestamp}-${plan.workflow}.json`); + fs.writeFileSync(target, `${JSON.stringify({ ...plan, createdAt: new Date().toISOString() }, null, 2)}\n`); + return target; +} + +export const providerCommandPattern = PROVIDER_COMMAND_PATTERN; diff --git a/scripts/productivity-workflow.mjs b/scripts/productivity-workflow.mjs new file mode 100644 index 000000000..4330e4d02 --- /dev/null +++ b/scripts/productivity-workflow.mjs @@ -0,0 +1,150 @@ +#!/usr/bin/env node +import fs from "node:fs"; +import path from "node:path"; +import { + analyzeFailureText, + buildWorkflowPlan, + readChangeScope, + runLocalChecks, + scanOperatorBacklog, + writeWorkflowEvidence, +} from "./productivity-core.mjs"; + +const workflows = new Set([ + "flightplan", + "triage", + "clinical-proof", + "design-sweep", + "rag-lab", + "operator-closeout", + "lifecycle", +]); + +function usage() { + console.log(`Usage: node scripts/productivity-workflow.mjs [options] + +Workflows: flightplan, triage, clinical-proof, design-sweep, rag-lab, operator-closeout, lifecycle +Options: + --files Plan for explicit paths instead of the current change + --phase Lifecycle phase: status, start, handoff, landed, cleanup + --log Triage a saved failure log + --run Execute local/offline checks only + --json Emit structured JSON + --write-evidence Save the plan under .local/workflow-evidence + --help Show this help + +Provider-backed commands are never executed by this tool.`); +} + +function parseArgs(argv) { + const [workflow, ...tokens] = argv; + const options = { + workflow, + files: undefined, + phase: "status", + log: undefined, + run: false, + json: false, + writeEvidence: false, + }; + for (let index = 0; index < tokens.length; index += 1) { + const token = tokens[index]; + if (token === "--help" || token === "-h") options.help = true; + else if (token === "--run") options.run = true; + else if (token === "--json") options.json = true; + else if (token === "--write-evidence") options.writeEvidence = true; + else if (token === "--files") + options.files = (tokens[++index] || "") + .split(",") + .map((item) => item.trim()) + .filter(Boolean); + else if (token === "--phase") options.phase = tokens[++index] || ""; + else if (token === "--log") options.log = tokens[++index] || ""; + else throw new Error(`Unknown option: ${token}`); + } + return options; +} + +function render(plan) { + console.log(`\n${plan.summary}`); + console.log(`Workflow: ${plan.workflow}`); + console.log(`Changed files: ${plan.files.length ? plan.files.join(", ") : "none detected"}`); + const activeRisks = Object.entries(plan.risks) + .filter(([, active]) => active) + .map(([name]) => name); + console.log(`Risk classes: ${activeRisks.length ? activeRisks.join(", ") : "none"}`); + if (plan.diagnosis) + console.log(`Diagnosis: ${plan.diagnosis.category} (${plan.diagnosis.confidence}) - ${plan.diagnosis.reason}`); + if (plan.operatorItems) { + console.log(`\nPending/operator candidates: ${plan.operatorItems.length}`); + for (const item of plan.operatorItems) console.log(`- ${item.source}:${item.line} ${item.text}`); + } + console.log("\nLocal/offline checks:"); + if (!plan.localChecks.length) console.log("- none selected"); + for (const item of plan.localChecks) console.log(`- ${item.command} — ${item.reason}`); + console.log("\nApproval-required commands:"); + if (!plan.approvalRequired.length) console.log("- none"); + for (const item of plan.approvalRequired) console.log(`- ${item.command} — ${item.reason}`); + console.log("\nRequired proof:"); + for (const item of plan.proof) console.log(`- ${item}`); +} + +function loadKnownFlakes(repoRoot) { + try { + const data = JSON.parse(fs.readFileSync(path.join(repoRoot, "tests", "flake-ledger.json"), "utf8")); + const records = Array.isArray(data) ? data : data.flakes || []; + return records.flatMap((entry) => + [entry.title, ...(entry.patterns || [])].filter(Boolean).map((pattern) => ({ id: entry.id, pattern })), + ); + } catch { + return []; + } +} + +function loadFailureText(options, repoRoot) { + if (options.log) return fs.readFileSync(path.resolve(repoRoot, options.log), "utf8"); + const marker = path.join(repoRoot, ".local", "workflow-last-failure.json"); + return fs.existsSync(marker) ? fs.readFileSync(marker, "utf8") : ""; +} + +function main() { + const options = parseArgs(process.argv.slice(2)); + if (options.help || !workflows.has(options.workflow)) { + usage(); + process.exit(options.help ? 0 : 1); + } + if (options.run && options.workflow === "operator-closeout") { + throw new Error( + "operator-closeout is plan-only; execute approved provider actions individually after confirmation.", + ); + } + + const repoRoot = process.cwd(); + const scope = readChangeScope(options.files, repoRoot); + const plan = buildWorkflowPlan(options.workflow, scope.files, { phase: options.phase }); + plan.scope = scope; + if (options.workflow === "triage") { + plan.diagnosis = analyzeFailureText(loadFailureText(options, repoRoot), loadKnownFlakes(repoRoot)); + } + if (options.workflow === "operator-closeout") plan.operatorItems = scanOperatorBacklog(repoRoot); + + if (options.json) console.log(JSON.stringify(plan, null, 2)); + else render(plan); + + if (options.writeEvidence) console.log(`\nEvidence: ${writeWorkflowEvidence(plan, repoRoot)}`); + if (options.run) { + const result = runLocalChecks(plan.localChecks, repoRoot); + if (result.status !== 0) { + console.error(`\n[workflow] stopped at: ${result.failed}`); + process.exit(result.status); + } + } +} + +try { + main(); + process.exit(0); +} catch (error) { + console.error(`[productivity-workflow] ${error instanceof Error ? error.message : String(error)}`); + process.exit(1); +} diff --git a/tests/productivity-workflow.test.ts b/tests/productivity-workflow.test.ts new file mode 100644 index 000000000..d1263e07a --- /dev/null +++ b/tests/productivity-workflow.test.ts @@ -0,0 +1,88 @@ +import { describe, expect, it } from "vitest"; + +import { + analyzeFailureText, + buildWorkflowPlan, + classifyRisks, + extractOperatorItemsFromText, + providerCommandPattern, +} from "../scripts/productivity-core.mjs"; +import { resolveExternalWorkflow, workflowRootCandidates } from "../scripts/external-workflow.mjs"; + +describe("productivity workflow planning", () => { + it("classifies UI, retrieval, clinical, and privacy risk from changed paths", () => { + const risks = classifyRisks([ + "src/components/ClinicalDashboard.tsx", + "src/lib/retrieval-selection.ts", + "src/lib/owner-scope.ts", + ]); + + expect(risks.ui).toBe(true); + expect(risks.retrieval).toBe(true); + expect(risks.clinical).toBe(true); + expect(risks.privacy).toBe(true); + }); + + it("never places provider commands in the executable local plan", () => { + const plan = buildWorkflowPlan("flightplan", [ + "src/app/api/answer/route.ts", + "supabase/migrations/20260717000000_example.sql", + ]); + + expect(plan.localChecks.some((item: { command: string }) => providerCommandPattern.test(item.command))).toBe(false); + expect(plan.approvalRequired.map((item: { command: string }) => item.command)).toContain( + "npm run check:supabase-project", + ); + expect(plan.approvalRequired.map((item: { command: string }) => item.command)).toContain( + "npm run eval:retrieval:quality", + ); + }); + + it("keeps the design sweep local and covers the established breakpoint proof", () => { + const plan = buildWorkflowPlan("design-sweep", ["src/components/ClinicalDashboard.tsx"]); + + expect(plan.approvalRequired).toEqual([]); + expect(plan.localChecks.map((item: { command: string }) => item.command)).toContain("npm run verify:ui"); + expect(plan.proof.join(" ")).toContain("320, 390, 639, 768, 1440, and 1920"); + }); + + it("rejects unknown lifecycle phases", () => { + expect(() => buildWorkflowPlan("lifecycle", [], { phase: "publish-everything" })).toThrow( + "Unknown lifecycle phase", + ); + }); + + it("classifies common failure signatures", () => { + expect(analyzeFailureText("Error: Cannot find module 'workflow-status.mjs'").category).toBe("environment"); + expect(analyzeFailureText("OPENAI_API_KEY missing").category).toBe("provider-or-configuration"); + expect(analyzeFailureText("AssertionError: expected 2 received 3").category).toBe("probable-regression"); + }); + + it("extracts only actionable operator markers", () => { + const items = extractOperatorItemsFromText( + "# Heading\n| Live gate | ⏳ pending | run later |\n- Operator-only: rotate key\n- completed", + "docs/example.md", + ); + + expect(items).toHaveLength(2); + expect(items[0]).toMatchObject({ source: "docs/example.md", line: 2 }); + }); +}); + +describe("external workflow portability", () => { + it("derives the shared workflow directory from the Git common directory", () => { + const candidates = workflowRootCandidates( + "C:\\Users\\example\\.codex\\worktrees\\1234\\Database", + "C:\\Dev\\Apps\\Database\\.git", + { NODE_ENV: "test" }, + ); + + expect(candidates.some((candidate) => candidate.replaceAll("\\", "/").endsWith("/Dev/Apps/.local-dev"))).toBe(true); + }); + + it("rejects unknown shared workflow names", () => { + expect(() => resolveExternalWorkflow("unknown", process.cwd(), "", { NODE_ENV: "test" })).toThrow( + "Unknown external workflow", + ); + }); +}); From a0b14518b28820aadbc808a3891a3d70b1ffb0fb Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:02:12 +0800 Subject: [PATCH 2/9] test(workflow): make resolver assertion cross-platform --- tests/productivity-workflow.test.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/productivity-workflow.test.ts b/tests/productivity-workflow.test.ts index d1263e07a..36e355f9d 100644 --- a/tests/productivity-workflow.test.ts +++ b/tests/productivity-workflow.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import path from "node:path"; import { analyzeFailureText, @@ -71,13 +72,13 @@ describe("productivity workflow planning", () => { describe("external workflow portability", () => { it("derives the shared workflow directory from the Git common directory", () => { - const candidates = workflowRootCandidates( - "C:\\Users\\example\\.codex\\worktrees\\1234\\Database", - "C:\\Dev\\Apps\\Database\\.git", - { NODE_ENV: "test" }, - ); + const root = path.parse(process.cwd()).root; + const cwd = path.join(root, "tmp", "codex", "worktrees", "1234", "Database"); + const gitCommonDir = path.join(root, "workspace", "Apps", "Database", ".git"); + const candidates = workflowRootCandidates(cwd, gitCommonDir, { NODE_ENV: "test" }); + const expected = path.join(root, "workspace", "Apps", ".local-dev"); - expect(candidates.some((candidate) => candidate.replaceAll("\\", "/").endsWith("/Dev/Apps/.local-dev"))).toBe(true); + expect(candidates).toContain(path.normalize(expected)); }); it("rejects unknown shared workflow names", () => { From 00fbc3803c6796d466095b3d6a8f7c9be4fdef2d Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:03:57 +0800 Subject: [PATCH 3/9] docs(workflow): clarify task-start preflight source --- .agents/skills/session-lifecycle/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.agents/skills/session-lifecycle/SKILL.md b/.agents/skills/session-lifecycle/SKILL.md index 2366083dc..8d89067ab 100644 --- a/.agents/skills/session-lifecycle/SKILL.md +++ b/.agents/skills/session-lifecycle/SKILL.md @@ -7,7 +7,7 @@ description: Manage Database repository work safely from task start through veri 1. Select the phase and generate its plan: `npm run workflow:lifecycle -- --phase ` -2. At task start, run the mandatory `start-codex-task.ps1` preflight, read applicable `AGENTS.md`, and preserve all existing work. Do not use the shared stash across worktrees. +2. At task start, run the mandatory task-start preflight command configured by the active host or root `AGENTS.md`, then read applicable repo instructions and preserve all existing work. On this Windows Codex installation, resolve the installation-managed script from the user profile (for example, `& (Join-Path $env:USERPROFILE '.codex\scripts\start-codex-task.ps1') -TaskSlug `); it is intentionally not repo-local. Do not use the shared stash across worktrees. 3. During work, recheck branch and status before edits and before handoff. Use `database-flightplan` for non-trivial change verification. 4. At handoff, stage explicit coherent paths only, verify locally, and inspect the staged diff. Commit, push, PR creation, hosted CI, and merge remain authorization-bound. 5. After a squash merge, verify the reviewed content against fetched `origin/main`; do not rely on ancestry alone. Check for late orphaned commits before cleanup. From b52112df6aa36311d7420189064acd79dcf2c3f5 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:11:22 +0800 Subject: [PATCH 4/9] fix(workflow): classify answer routes as database-scoped --- scripts/productivity-core.mjs | 6 +++++- tests/productivity-workflow.test.ts | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/productivity-core.mjs b/scripts/productivity-core.mjs index 872bf5602..a6845e95b 100644 --- a/scripts/productivity-core.mjs +++ b/scripts/productivity-core.mjs @@ -14,7 +14,11 @@ const RISK_PATTERNS = { /^tests\/ui-.*\.spec\.ts$/, /^playwright.*\.ts$/, ], - database: ["supabase", "src/lib/supabase", /^src\/app\/api\/(?:documents|images|ingestion|jobs|search|upload)/], + database: [ + "supabase", + "src/lib/supabase", + /^src\/app\/api\/(?:answer|documents|images|ingestion|jobs|search|upload)/, + ], retrieval: [ /^src\/lib\/(?:rag|retrieval|ranking|clinical-search|smart-rag|citations|evidence|source|chunking)/, /^src\/app\/api\/(?:answer|search)/, diff --git a/tests/productivity-workflow.test.ts b/tests/productivity-workflow.test.ts index 36e355f9d..0cb20a744 100644 --- a/tests/productivity-workflow.test.ts +++ b/tests/productivity-workflow.test.ts @@ -39,6 +39,16 @@ describe("productivity workflow planning", () => { ); }); + it("treats answer routes as database-scoped without needing a migration path", () => { + const files = ["src/app/api/answer/route.ts"]; + const plan = buildWorkflowPlan("flightplan", files); + + expect(classifyRisks(files).database).toBe(true); + expect(plan.approvalRequired.map((item: { command: string }) => item.command)).toContain( + "npm run check:supabase-project", + ); + }); + it("keeps the design sweep local and covers the established breakpoint proof", () => { const plan = buildWorkflowPlan("design-sweep", ["src/components/ClinicalDashboard.tsx"]); From d52a2273d427e6ba9bbd964b7b0ba923dcefeb37 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:20:47 +0800 Subject: [PATCH 5/9] fix(workflow): close review safety gaps --- .agents/skills/clinical-change-proof/SKILL.md | 2 +- .gitignore | 1 + docs/branch-review-ledger.md | 1 + scripts/ci-change-scope.mjs | 4 +- scripts/productivity-core.mjs | 7 ++- scripts/productivity-workflow.mjs | 25 +++++--- tests/productivity-workflow.test.ts | 60 +++++++++++++++++++ 7 files changed, 87 insertions(+), 13 deletions(-) diff --git a/.agents/skills/clinical-change-proof/SKILL.md b/.agents/skills/clinical-change-proof/SKILL.md index 613f73335..d5634bd7b 100644 --- a/.agents/skills/clinical-change-proof/SKILL.md +++ b/.agents/skills/clinical-change-proof/SKILL.md @@ -16,5 +16,5 @@ description: Build evidence for Database changes affecting clinical output, inge - rollback or feature-disable behavior is documented; - affected behavior has focused regression coverage. 5. Run the local checks in the plan, narrowest first. The offline RAG contract is mandatory when retrieval or answer behavior is involved. -6. Present every live Supabase, OpenAI, hosted CI, or production-readiness command as a separate approval request. Never bundle approval implicitly into a local gate. +6. Treat every command in the plan's `approvalRequired` list as an independent confirmation-required action, including retrieval, deployment, release, remote Git, hosted CI, Supabase, OpenAI, and production-readiness commands. Never bundle approvals, infer approval from another action, or execute one indirectly through a local gate or wrapper. 7. Complete the Clinical Governance Preflight and report evidence, gaps, rollback, and any SaMD implication. diff --git a/.gitignore b/.gitignore index b17f695ae..76c42fe03 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ /.codex/ /.agents/* !/.agents/plugins/ +/.agents/plugins/** !/.agents/plugins/marketplace.json !/.agents/plugins/api_marketplace.json !/.agents/skills/ diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index c63f034d3..0104a19fe 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -569,3 +569,4 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-17 | codex/historical-branch-cleanup-20260717 | e36ac0c6628264c7ed6c494a597a62d0214b68f6 | branch-cleanup and historical-content recovery | Completed the pending historical cleanup: deleted 55 exact-SHA remote refs and 20 redundant local refs, removed nine clean merged worktrees, preserved every dirty, active, open-PR, or patch-unique worktree, and recovered the still-useful governance incident runbook from `codex/domain-1-governance-remediation`. Historical code changes were either tied to merged PRs or reviewed as superseded by current implementations; open PRs #699, #700, #702, and #704 remain protected. | Fresh `git fetch --prune`; GitHub PR inventory and exact commit-to-merged-PR associations; exact remote SHA rechecks before deletion; cherry-pick-aware logs; two-dot tree and branch-only-file review; Codex task-to-worktree cross-check; focused documentation validation recorded in the cleanup PR. No OpenAI, Supabase, production-data, or live clinical workflow was run. | | 2026-07-17 | PR #704 / codex/scroll-geometry-stability-20260717 | 35e74ddbd61bacc5b34f06efbd58091f092665fd | nested scroll-source review follow-up | Confirmed the outside-diff CodeRabbit finding: the standalone shell shared one intent history across main and descendant scroll containers, so a switch from a deep main offset to a near-zero nested offset could falsely reveal chrome. Scroll metrics now identify their source, source changes rebase direction and travel while preserving visibility, and unit/UI regressions cover the switch. No unresolved actionable review finding remains. | Focused Vitest 9/9; TypeScript; scoped ESLint; Prettier; `git diff --check`. Exact-head hosted CI and UI remain required after push. No Supabase/OpenAI/live-provider checks run. | | 2026-07-17 | codex/scrolling-cleanup-20260717 | ff77cd06c + latest origin/main sync | cross-page scrolling and interaction stability review | Fixed two confirmed P2 defects: desktop action-popup placement performed synchronous geometry work for every captured scroll event, and submitted differential searches with zero document matches fell back to the home state. Placement is now coalesced per animation frame with passive scroll listeners, and submitted empty-evidence results remain visible. Hardened three popup/navigation browser helpers that reproduced hydration timing failures. No other high-confidence defect remains in the scoped diff. | Scroll-focused Chromium 28/28; final affected Chromium 5/5; source regressions 2/2; scoped ESLint; Prettier; TypeScript and production build passed before the final upstream-only sync; `git diff --check`. The aggregate local Vitest/UI runs were affected by concurrent-worktree resource contention, so exact-head hosted CI remains required before merge. No Supabase/OpenAI/live-provider checks run. | +| 2026-07-17 | PR #713 / codex/chat-workflow-ideas-0916 | b52112df6aa36311d7420189064acd79dcf2c3f5 + reviewed follow-up diff | workflow toolkit review follow-up | Fixed all 12 actionable Codex and CodeRabbit threads: cross-platform path fixtures, installation-managed preflight guidance, answer-route database scoping, per-command approval boundaries, plugin-ignore narrowing, isolated CI-scope proof, remote-Git command guarding, repository-skill verification classification, `TypeError` diagnosis, strict CLI option values, and machine-parseable JSON evidence output. No unresolved actionable finding remains in the reviewed scope. | `npm run verify:cheap` passed with 273 files and 2,599 tests; focused toolkit Vitest 13/13; CI-scope self-test; plugin-ignore proof; `git diff --check`. Exact-head hosted CI remains required after the follow-up push. No Supabase, OpenAI, or other live product-provider command was run. | diff --git a/scripts/ci-change-scope.mjs b/scripts/ci-change-scope.mjs index e5eb96505..10cee0418 100644 --- a/scripts/ci-change-scope.mjs +++ b/scripts/ci-change-scope.mjs @@ -463,9 +463,9 @@ function selfTest() { docs_only: false, build_changed: false, }); - assertScope("repo-skill", [".agents/skills/database-flightplan/SKILL.md", "scripts/productivity-core.mjs"], { + assertScope("repo-skill", [".agents/skills/database-flightplan/SKILL.md"], { workflow_changed: true, - source_changed: true, + source_changed: false, docs_only: false, build_changed: false, }); diff --git a/scripts/productivity-core.mjs b/scripts/productivity-core.mjs index a6845e95b..4fb255aa7 100644 --- a/scripts/productivity-core.mjs +++ b/scripts/productivity-core.mjs @@ -3,7 +3,7 @@ import fs from "node:fs"; import path from "node:path"; const PROVIDER_COMMAND_PATTERN = - /(?:check:supabase-project|check:production-readiness|verify:release(?:\s|$)|eval:retrieval:quality|eval:rag(?:\s|$)|eval:quality(?:\s|$)|gh\s|railway\s|supabase\s)/i; + /(?:check:supabase-project|check:production-readiness|verify:release(?:\s|$)|eval:retrieval:quality|eval:rag(?:\s|$)|eval:quality(?:\s|$)|git\s+(?:fetch|ls-remote|pull|push)(?:\s|$)|gh\s|glab\s|railway\s|supabase\s)/i; const RISK_PATTERNS = { ui: [ @@ -91,7 +91,8 @@ export function classifyRisks(files = []) { normalized.some((file) => patterns.some((pattern) => matches(file, pattern))), ]), ); - risks.docsOnly = normalized.length > 0 && normalized.every((file) => /(?:^docs\/|\.mdx?$)/i.test(file)); + risks.docsOnly = + normalized.length > 0 && normalized.every((file) => /(?:^docs\/|\.mdx?$)/i.test(file)) && !risks.workflow; return risks; } @@ -297,7 +298,7 @@ export function analyzeFailureText(text = "", knownFlakes = []) { reason: "Timeout or runtime process failure; inspect active processes and artifacts.", }; } - if (/assertionerror|expected .* received|tests? failed|type error|eslint.*error|syntaxerror/.test(lower)) { + if (/assertionerror|expected .* received|tests? failed|type ?error|eslint.*error|syntaxerror/.test(lower)) { return { category: "probable-regression", confidence: "medium", diff --git a/scripts/productivity-workflow.mjs b/scripts/productivity-workflow.mjs index 4330e4d02..9ef4ce60c 100644 --- a/scripts/productivity-workflow.mjs +++ b/scripts/productivity-workflow.mjs @@ -49,17 +49,23 @@ function parseArgs(argv) { }; for (let index = 0; index < tokens.length; index += 1) { const token = tokens[index]; + const nextValue = () => { + const value = tokens[index + 1]; + if (!value || value.startsWith("-")) throw new Error(`Missing value for ${token}`); + index += 1; + return value; + }; if (token === "--help" || token === "-h") options.help = true; else if (token === "--run") options.run = true; else if (token === "--json") options.json = true; else if (token === "--write-evidence") options.writeEvidence = true; else if (token === "--files") - options.files = (tokens[++index] || "") + options.files = nextValue() .split(",") .map((item) => item.trim()) .filter(Boolean); - else if (token === "--phase") options.phase = tokens[++index] || ""; - else if (token === "--log") options.log = tokens[++index] || ""; + else if (token === "--phase") options.phase = nextValue(); + else if (token === "--log") options.log = nextValue(); else throw new Error(`Unknown option: ${token}`); } return options; @@ -118,6 +124,9 @@ function main() { "operator-closeout is plan-only; execute approved provider actions individually after confirmation.", ); } + if (options.json && options.run) { + throw new Error("--json cannot be combined with --run because executed checks write non-JSON output."); + } const repoRoot = process.cwd(); const scope = readChangeScope(options.files, repoRoot); @@ -128,10 +137,12 @@ function main() { } if (options.workflow === "operator-closeout") plan.operatorItems = scanOperatorBacklog(repoRoot); - if (options.json) console.log(JSON.stringify(plan, null, 2)); - else render(plan); - - if (options.writeEvidence) console.log(`\nEvidence: ${writeWorkflowEvidence(plan, repoRoot)}`); + const evidencePath = options.writeEvidence ? writeWorkflowEvidence(plan, repoRoot) : undefined; + if (options.json) console.log(JSON.stringify(evidencePath ? { ...plan, evidencePath } : plan, null, 2)); + else { + render(plan); + if (evidencePath) console.log(`\nEvidence: ${evidencePath}`); + } if (options.run) { const result = runLocalChecks(plan.localChecks, repoRoot); if (result.status !== 0) { diff --git a/tests/productivity-workflow.test.ts b/tests/productivity-workflow.test.ts index 0cb20a744..6e681ce8c 100644 --- a/tests/productivity-workflow.test.ts +++ b/tests/productivity-workflow.test.ts @@ -1,4 +1,6 @@ import { describe, expect, it } from "vitest"; +import { spawnSync } from "node:child_process"; +import fs from "node:fs"; import path from "node:path"; import { @@ -10,6 +12,15 @@ import { } from "../scripts/productivity-core.mjs"; import { resolveExternalWorkflow, workflowRootCandidates } from "../scripts/external-workflow.mjs"; +const workflowScript = path.resolve(process.cwd(), "scripts", "productivity-workflow.mjs"); + +function runWorkflow(args: string[]) { + return spawnSync(process.execPath, [workflowScript, ...args], { + cwd: process.cwd(), + encoding: "utf8", + }); +} + describe("productivity workflow planning", () => { it("classifies UI, retrieval, clinical, and privacy risk from changed paths", () => { const risks = classifyRisks([ @@ -37,6 +48,8 @@ describe("productivity workflow planning", () => { expect(plan.approvalRequired.map((item: { command: string }) => item.command)).toContain( "npm run eval:retrieval:quality", ); + expect(providerCommandPattern.test("git push origin feature")).toBe(true); + expect(providerCommandPattern.test("glab mr merge 123")).toBe(true); }); it("treats answer routes as database-scoped without needing a migration path", () => { @@ -57,6 +70,13 @@ describe("productivity workflow planning", () => { expect(plan.proof.join(" ")).toContain("320, 390, 639, 768, 1440, and 1920"); }); + it("treats repository skill instructions as workflow changes rather than docs-only", () => { + const plan = buildWorkflowPlan("flightplan", [".agents/skills/database-flightplan/SKILL.md"]); + + expect(plan.risks).toMatchObject({ workflow: true, docsOnly: false }); + expect(plan.localChecks.map((item: { command: string }) => item.command)).toContain("npm run verify:pr-local"); + }); + it("rejects unknown lifecycle phases", () => { expect(() => buildWorkflowPlan("lifecycle", [], { phase: "publish-everything" })).toThrow( "Unknown lifecycle phase", @@ -67,6 +87,7 @@ describe("productivity workflow planning", () => { expect(analyzeFailureText("Error: Cannot find module 'workflow-status.mjs'").category).toBe("environment"); expect(analyzeFailureText("OPENAI_API_KEY missing").category).toBe("provider-or-configuration"); expect(analyzeFailureText("AssertionError: expected 2 received 3").category).toBe("probable-regression"); + expect(analyzeFailureText("TypeError: value is not iterable").category).toBe("probable-regression"); }); it("extracts only actionable operator markers", () => { @@ -80,6 +101,45 @@ describe("productivity workflow planning", () => { }); }); +describe("productivity workflow CLI", () => { + it("rejects option flags where a required value is missing", () => { + const result = runWorkflow(["flightplan", "--files", "--json"]); + + expect(result.status).toBe(1); + expect(result.stdout).toBe(""); + expect(result.stderr).toContain("Missing value for --files"); + }); + + it("rejects JSON execution before any local checks can write mixed output", () => { + const result = runWorkflow(["flightplan", "--files", "docs/example.md", "--json", "--run"]); + + expect(result.status).toBe(1); + expect(result.stdout).toBe(""); + expect(result.stderr).toContain("--json cannot be combined with --run"); + }); + + it("keeps JSON evidence output parseable and cleans its test artifact", () => { + const evidenceDirectory = path.join(process.cwd(), ".local", "workflow-evidence"); + const before = new Set(fs.existsSync(evidenceDirectory) ? fs.readdirSync(evidenceDirectory) : []); + + try { + const result = runWorkflow(["flightplan", "--files", "docs/example.md", "--json", "--write-evidence"]); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + const payload = JSON.parse(result.stdout); + expect(payload.evidencePath).toMatch(/workflow-evidence/); + expect(fs.existsSync(payload.evidencePath)).toBe(true); + } finally { + if (fs.existsSync(evidenceDirectory)) { + for (const entry of fs.readdirSync(evidenceDirectory)) { + if (!before.has(entry)) fs.rmSync(path.join(evidenceDirectory, entry), { force: true }); + } + } + } + }); +}); + describe("external workflow portability", () => { it("derives the shared workflow directory from the Git common directory", () => { const root = path.parse(process.cwd()).root; From a1732cf9938a8ee3a6ffe204e4f1cba169d1364a Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:25:01 +0800 Subject: [PATCH 6/9] fix(workflow): preserve RAG lab approval gates --- docs/branch-review-ledger.md | 2 +- scripts/productivity-core.mjs | 1 + tests/productivity-workflow.test.ts | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 0104a19fe..9f6e8acbc 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -569,4 +569,4 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-17 | codex/historical-branch-cleanup-20260717 | e36ac0c6628264c7ed6c494a597a62d0214b68f6 | branch-cleanup and historical-content recovery | Completed the pending historical cleanup: deleted 55 exact-SHA remote refs and 20 redundant local refs, removed nine clean merged worktrees, preserved every dirty, active, open-PR, or patch-unique worktree, and recovered the still-useful governance incident runbook from `codex/domain-1-governance-remediation`. Historical code changes were either tied to merged PRs or reviewed as superseded by current implementations; open PRs #699, #700, #702, and #704 remain protected. | Fresh `git fetch --prune`; GitHub PR inventory and exact commit-to-merged-PR associations; exact remote SHA rechecks before deletion; cherry-pick-aware logs; two-dot tree and branch-only-file review; Codex task-to-worktree cross-check; focused documentation validation recorded in the cleanup PR. No OpenAI, Supabase, production-data, or live clinical workflow was run. | | 2026-07-17 | PR #704 / codex/scroll-geometry-stability-20260717 | 35e74ddbd61bacc5b34f06efbd58091f092665fd | nested scroll-source review follow-up | Confirmed the outside-diff CodeRabbit finding: the standalone shell shared one intent history across main and descendant scroll containers, so a switch from a deep main offset to a near-zero nested offset could falsely reveal chrome. Scroll metrics now identify their source, source changes rebase direction and travel while preserving visibility, and unit/UI regressions cover the switch. No unresolved actionable review finding remains. | Focused Vitest 9/9; TypeScript; scoped ESLint; Prettier; `git diff --check`. Exact-head hosted CI and UI remain required after push. No Supabase/OpenAI/live-provider checks run. | | 2026-07-17 | codex/scrolling-cleanup-20260717 | ff77cd06c + latest origin/main sync | cross-page scrolling and interaction stability review | Fixed two confirmed P2 defects: desktop action-popup placement performed synchronous geometry work for every captured scroll event, and submitted differential searches with zero document matches fell back to the home state. Placement is now coalesced per animation frame with passive scroll listeners, and submitted empty-evidence results remain visible. Hardened three popup/navigation browser helpers that reproduced hydration timing failures. No other high-confidence defect remains in the scoped diff. | Scroll-focused Chromium 28/28; final affected Chromium 5/5; source regressions 2/2; scoped ESLint; Prettier; TypeScript and production build passed before the final upstream-only sync; `git diff --check`. The aggregate local Vitest/UI runs were affected by concurrent-worktree resource contention, so exact-head hosted CI remains required before merge. No Supabase/OpenAI/live-provider checks run. | -| 2026-07-17 | PR #713 / codex/chat-workflow-ideas-0916 | b52112df6aa36311d7420189064acd79dcf2c3f5 + reviewed follow-up diff | workflow toolkit review follow-up | Fixed all 12 actionable Codex and CodeRabbit threads: cross-platform path fixtures, installation-managed preflight guidance, answer-route database scoping, per-command approval boundaries, plugin-ignore narrowing, isolated CI-scope proof, remote-Git command guarding, repository-skill verification classification, `TypeError` diagnosis, strict CLI option values, and machine-parseable JSON evidence output. No unresolved actionable finding remains in the reviewed scope. | `npm run verify:cheap` passed with 273 files and 2,599 tests; focused toolkit Vitest 13/13; CI-scope self-test; plugin-ignore proof; `git diff --check`. Exact-head hosted CI remains required after the follow-up push. No Supabase, OpenAI, or other live product-provider command was run. | +| 2026-07-17 | PR #713 / codex/chat-workflow-ideas-0916 | b52112df6aa36311d7420189064acd79dcf2c3f5 + reviewed follow-up diff | workflow toolkit review follow-up | Fixed all 13 actionable Codex and CodeRabbit threads: cross-platform path fixtures, installation-managed preflight guidance, answer-route database scoping, per-command approval boundaries, plugin-ignore narrowing, isolated CI-scope proof, remote-Git command guarding, repository-skill verification classification, `TypeError` diagnosis, strict CLI option values, machine-parseable JSON evidence output, and preservation of baseline database/clinical approval gates in the RAG lab. No unresolved actionable finding remains in the reviewed scope. | `npm run verify:cheap` passed with 273 files and 2,599 tests; focused toolkit Vitest 14/14; CI-scope self-test; plugin-ignore proof; `git diff --check`. Exact-head hosted CI remains required after the follow-up push. No Supabase, OpenAI, or other live product-provider command was run. | diff --git a/scripts/productivity-core.mjs b/scripts/productivity-core.mjs index 4fb255aa7..70c45c21b 100644 --- a/scripts/productivity-core.mjs +++ b/scripts/productivity-core.mjs @@ -215,6 +215,7 @@ export function buildWorkflowPlan(workflow, files = [], options = {}) { ]; approvalRequired = uniqueBy( [ + ...approvalRequired, check("npm run eval:retrieval:quality", "Require 36/36 against the live corpus after explicit approval."), check("npm run eval:rag -- --limit 15", "Sample live answer generation after explicit approval."), check("npm run eval:quality -- --rag-only", "Compare grounded-answer invariants after explicit approval."), diff --git a/tests/productivity-workflow.test.ts b/tests/productivity-workflow.test.ts index 6e681ce8c..2880de1ec 100644 --- a/tests/productivity-workflow.test.ts +++ b/tests/productivity-workflow.test.ts @@ -62,6 +62,15 @@ describe("productivity workflow planning", () => { ); }); + it("preserves database and clinical approval gates in the RAG lab", () => { + const plan = buildWorkflowPlan("rag-lab", ["src/app/api/answer/route.ts"]); + const commands = plan.approvalRequired.map((item: { command: string }) => item.command); + + expect(commands).toContain("npm run check:supabase-project"); + expect(commands).toContain("npm run check:production-readiness"); + expect(commands).toContain("npm run eval:retrieval:quality"); + }); + it("keeps the design sweep local and covers the established breakpoint proof", () => { const plan = buildWorkflowPlan("design-sweep", ["src/components/ClinicalDashboard.tsx"]); From 07b4af045e9cf976df8a304012e65ce92d4c7195 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:29:21 +0800 Subject: [PATCH 7/9] fix(workflow): align database route classification --- docs/branch-review-ledger.md | 2 +- scripts/productivity-core.mjs | 2 +- tests/productivity-workflow.test.ts | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 9f6e8acbc..b18697ac0 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -569,4 +569,4 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-17 | codex/historical-branch-cleanup-20260717 | e36ac0c6628264c7ed6c494a597a62d0214b68f6 | branch-cleanup and historical-content recovery | Completed the pending historical cleanup: deleted 55 exact-SHA remote refs and 20 redundant local refs, removed nine clean merged worktrees, preserved every dirty, active, open-PR, or patch-unique worktree, and recovered the still-useful governance incident runbook from `codex/domain-1-governance-remediation`. Historical code changes were either tied to merged PRs or reviewed as superseded by current implementations; open PRs #699, #700, #702, and #704 remain protected. | Fresh `git fetch --prune`; GitHub PR inventory and exact commit-to-merged-PR associations; exact remote SHA rechecks before deletion; cherry-pick-aware logs; two-dot tree and branch-only-file review; Codex task-to-worktree cross-check; focused documentation validation recorded in the cleanup PR. No OpenAI, Supabase, production-data, or live clinical workflow was run. | | 2026-07-17 | PR #704 / codex/scroll-geometry-stability-20260717 | 35e74ddbd61bacc5b34f06efbd58091f092665fd | nested scroll-source review follow-up | Confirmed the outside-diff CodeRabbit finding: the standalone shell shared one intent history across main and descendant scroll containers, so a switch from a deep main offset to a near-zero nested offset could falsely reveal chrome. Scroll metrics now identify their source, source changes rebase direction and travel while preserving visibility, and unit/UI regressions cover the switch. No unresolved actionable review finding remains. | Focused Vitest 9/9; TypeScript; scoped ESLint; Prettier; `git diff --check`. Exact-head hosted CI and UI remain required after push. No Supabase/OpenAI/live-provider checks run. | | 2026-07-17 | codex/scrolling-cleanup-20260717 | ff77cd06c + latest origin/main sync | cross-page scrolling and interaction stability review | Fixed two confirmed P2 defects: desktop action-popup placement performed synchronous geometry work for every captured scroll event, and submitted differential searches with zero document matches fell back to the home state. Placement is now coalesced per animation frame with passive scroll listeners, and submitted empty-evidence results remain visible. Hardened three popup/navigation browser helpers that reproduced hydration timing failures. No other high-confidence defect remains in the scoped diff. | Scroll-focused Chromium 28/28; final affected Chromium 5/5; source regressions 2/2; scoped ESLint; Prettier; TypeScript and production build passed before the final upstream-only sync; `git diff --check`. The aggregate local Vitest/UI runs were affected by concurrent-worktree resource contention, so exact-head hosted CI remains required before merge. No Supabase/OpenAI/live-provider checks run. | -| 2026-07-17 | PR #713 / codex/chat-workflow-ideas-0916 | b52112df6aa36311d7420189064acd79dcf2c3f5 + reviewed follow-up diff | workflow toolkit review follow-up | Fixed all 13 actionable Codex and CodeRabbit threads: cross-platform path fixtures, installation-managed preflight guidance, answer-route database scoping, per-command approval boundaries, plugin-ignore narrowing, isolated CI-scope proof, remote-Git command guarding, repository-skill verification classification, `TypeError` diagnosis, strict CLI option values, machine-parseable JSON evidence output, and preservation of baseline database/clinical approval gates in the RAG lab. No unresolved actionable finding remains in the reviewed scope. | `npm run verify:cheap` passed with 273 files and 2,599 tests; focused toolkit Vitest 14/14; CI-scope self-test; plugin-ignore proof; `git diff --check`. Exact-head hosted CI remains required after the follow-up push. No Supabase, OpenAI, or other live product-provider command was run. | +| 2026-07-17 | PR #713 / codex/chat-workflow-ideas-0916 | b52112df6aa36311d7420189064acd79dcf2c3f5 + reviewed follow-up diff | workflow toolkit review follow-up | Fixed all 14 actionable Codex and CodeRabbit threads: cross-platform path fixtures, installation-managed preflight guidance, complete Supabase-backed API database scoping, per-command approval boundaries, plugin-ignore narrowing, isolated CI-scope proof, remote-Git command guarding, repository-skill verification classification, `TypeError` diagnosis, strict CLI option values, machine-parseable JSON evidence output, and preservation of baseline database/clinical approval gates in the RAG lab. No unresolved actionable finding remains in the reviewed scope. | `npm run verify:cheap` passed with 273 files and 2,599 tests; focused toolkit Vitest 20/20; CI-scope self-test; plugin-ignore proof; `git diff --check`. Exact-head hosted CI remains required after the follow-up push. No Supabase, OpenAI, or other live product-provider command was run. | diff --git a/scripts/productivity-core.mjs b/scripts/productivity-core.mjs index 70c45c21b..43f634bdc 100644 --- a/scripts/productivity-core.mjs +++ b/scripts/productivity-core.mjs @@ -17,7 +17,7 @@ const RISK_PATTERNS = { database: [ "supabase", "src/lib/supabase", - /^src\/app\/api\/(?:answer|documents|images|ingestion|jobs|search|upload)/, + /^src\/app\/api\/(?:answer|differentials|documents|eval-cases|health|images|ingestion|jobs|medications|registry|search|setup-status|upload)/, ], retrieval: [ /^src\/lib\/(?:rag|retrieval|ranking|clinical-search|smart-rag|citations|evidence|source|chunking)/, diff --git a/tests/productivity-workflow.test.ts b/tests/productivity-workflow.test.ts index 2880de1ec..219a1c5c4 100644 --- a/tests/productivity-workflow.test.ts +++ b/tests/productivity-workflow.test.ts @@ -62,6 +62,18 @@ describe("productivity workflow planning", () => { ); }); + it.each(["differentials", "eval-cases", "health", "medications", "registry", "setup-status"])( + "treats the %s API route as database-scoped", + (route) => { + const plan = buildWorkflowPlan("flightplan", [`src/app/api/${route}/route.ts`]); + + expect(plan.risks.database).toBe(true); + expect(plan.approvalRequired.map((item: { command: string }) => item.command)).toContain( + "npm run check:supabase-project", + ); + }, + ); + it("preserves database and clinical approval gates in the RAG lab", () => { const plan = buildWorkflowPlan("rag-lab", ["src/app/api/answer/route.ts"]); const commands = plan.approvalRequired.map((item: { command: string }) => item.command); From d22a9894eb54162989948c0faa632322438b203f Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sat, 18 Jul 2026 17:07:51 +0800 Subject: [PATCH 8/9] chore(skills): add database workflows skill index --- .agents/skills/database-skills.md | 20 ++++++++++++++ .agents/skills/workflows/SKILL.md | 30 +++++++++++++++++++++ .agents/skills/workflows/agents/openai.yaml | 4 +++ 3 files changed, 54 insertions(+) create mode 100644 .agents/skills/database-skills.md create mode 100644 .agents/skills/workflows/SKILL.md create mode 100644 .agents/skills/workflows/agents/openai.yaml diff --git a/.agents/skills/database-skills.md b/.agents/skills/database-skills.md new file mode 100644 index 000000000..2b6af658a --- /dev/null +++ b/.agents/skills/database-skills.md @@ -0,0 +1,20 @@ +# Database skills catalog + +This repository has the following Database-specific skills. + +## Workflow and repo orchestration skills + +- `database-flightplan` — create a risk-scoped verification plan before non-trivial work. +- `verify-triage-fix` — diagnose and fix local verification failures with minimal repros. +- `clinical-change-proof` — assemble clinical/medical safety and production-readiness evidence. +- `live-design-sweep` — inspect the running app across routes, breakpoints, accessibility modes, and interactions. +- `rag-change-lab` — validate retrieval/ranking/grounding changes with focused offline checks. +- `operator-closeout` — turn operator/provisioning/debt tasks into an ordered, approval-gated execution plan. +- `session-lifecycle` — manage safe task start/handoff/cleanup and merge-proof transitions. +- `workflows` — this new skill: indexed reference describing each Database workflow skill and when to use it. + +## Notes + +- These are the skills currently present in `.agents/skills` for this Database worktree. +- Use these skills as lightweight orchestration and evidence-first planning helpers. +- The first step for most non-trivial tasks remains `start-codex-task.ps1` (as required by AGENTS instructions). diff --git a/.agents/skills/workflows/SKILL.md b/.agents/skills/workflows/SKILL.md new file mode 100644 index 000000000..3cbe11c14 --- /dev/null +++ b/.agents/skills/workflows/SKILL.md @@ -0,0 +1,30 @@ +--- +name: workflows +description: List all Database workflow skills, summarize their purpose, and recommend the right workflow for the current change. +--- + +# Database Workflows + +## What this skill covers + +Use this skill when you need a quick index of Database-specific workflow skills and a reminder of what each one validates. + +## Available workflow skills + +- `database-flightplan`: Risk classification and local/offline verification planning for non-trivial changes. +- `verify-triage-fix`: Stepwise local failure triage for lint/typecheck/tests/build/playwright/offline checks. +- `clinical-change-proof`: Clinical, answer-governance, safety, privacy, and source-governance evidence planning. +- `live-design-sweep`: App-wide live route/design/accessibility verification across breakpoints and interaction states. +- `rag-change-lab`: Offline evaluation and migration-proofing for retrieval, classification, ranking, and grounding changes. +- `operator-closeout`: Conversion of pending deployment/operator/provisioning tasks into dependency-ordered execution batches. +- `session-lifecycle`: Start-to-handoff-to-cleanup orchestration with review/merge safety checks. + +## Recommended usage pattern + +1. Start with `database-flightplan` for non-trivial edits. +2. Use `verify-triage-fix` only when checks fail or hang. +3. Add `clinical-change-proof` for clinical retrieval, output, or source-governance risk. +4. Use `live-design-sweep` for UI/routing/interaction defects. +5. Use `rag-change-lab` for RAG behavior work. +6. Use `operator-closeout` for deferred operator or provider-work batches. +7. Use `session-lifecycle` for session-level handoff and cleanup. diff --git a/.agents/skills/workflows/agents/openai.yaml b/.agents/skills/workflows/agents/openai.yaml new file mode 100644 index 000000000..cb9580560 --- /dev/null +++ b/.agents/skills/workflows/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Database Workflows" + short_description: "Index Database workflow skills and choose the right one" + default_prompt: "Use $workflows to list all Database workflow skills with a concise explanation of what each does and when to run it." From cd89ae58f33f3393d9696385c230264e7df9838e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Jul 2026 12:38:40 +0000 Subject: [PATCH 9/9] fix: resolve merge conflict markers in ci-change-scope.mjs and branch-review-ledger.md --- docs/branch-review-ledger.md | 4 ---- scripts/ci-change-scope.mjs | 4 ---- 2 files changed, 8 deletions(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 243afceae..d34e98657 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -589,9 +589,6 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-17 | codex/chat-supabase-migration-preflight-b463 | f7c4e293ef35acc54f2b82bbccb2990d51289d5c | live production Supabase security, integrity, drift, and performance review plus remediation | Resolved and deployed the P2 retrieval-performance issue with `20260717160000_optimize_owner_public_retrieval`: owner/public filtering now happens in one scoped query and index-unit text/term candidates use separate GIN-friendly branches. Warm text retrieval improved from 1.269 seconds to 34 ms; warm index-unit retrieval completed in 36-39 ms (first cold run 2.376 seconds with 2,009 physical reads). No P0/P1 security, privacy, RLS, privilege, storage, migration-history, or integrity issue was found. | Isolated Docker schema replay; pre-deploy drift showed exactly four intended function changes; linked production push; post-deploy `No unexpected schema drift`; exact project and migration-history checks; security and performance advisors; live access-scope parity; bounded `EXPLAIN ANALYZE`; post-migration logs; focused Vitest 74/74; offline RAG fixtures 36/36 and contract tests 291/291; ESLint; TypeScript; function grants; production readiness. Full `verify:cheap` reached the 10-minute host timeout during broad Vitest and ended with EPIPE; focused and domain checks passed. No OpenAI calls, write load test, or backup/PITR restore test was performed. | | 2026-07-17 | codex/chat-forms-import-6914 | e5caaa46cad5fb9a937f1dc43312723799b98abb + working-tree diff | shared Forms/Services catalogue access and LOCAL_NO_AUTH_OWNER_ID review | Fixed one P1 availability/design defect: authenticated reads materialized a private copy of the shared catalogue on first access, creating drift, unnecessary writes, and possible registry-corpus side effects. Forms/Services now merge the reviewed shared baseline with private owner overrides for list, detail, and universal search; older partial overrides retain missing shared metadata; no registry GET seeds or embeds. Private rows and linked documents remain owner-scoped. The ignored local owner setting was corrected to the verified sole live-owner UUID and source validation now requires a UUID. No remaining high-confidence defect in scope. | Focused registry/universal Vitest initially exposed four local expectation/count mismatches; corrected registry suite passed 17/17 and registry/logging suite passed 23/23. Full TypeScript passed. `verify:cheap` passed runtime, action pins, sitemap, brand, type/icon/function guards, full lint, and TypeScript; full Vitest reached 2,588 passing with one stale logging-guard failure, which was fixed and focused-verified. The final full-suite rerun was terminated by the 5-minute host timeout without a reported assertion failure. `git diff --check` passed. No Supabase/OpenAI/provider call or schema/RLS mutation was run for this review. | | 2026-07-17 | codex/scrolling-cleanup-20260717 | ff77cd06c + latest origin/main sync | cross-page scrolling and interaction stability review | Fixed two confirmed P2 defects: desktop action-popup placement performed synchronous geometry work for every captured scroll event, and submitted differential searches with zero document matches fell back to the home state. Placement is now coalesced per animation frame with passive scroll listeners, and submitted empty-evidence results remain visible. Hardened three popup/navigation browser helpers that reproduced hydration timing failures. No other high-confidence defect remains in the scoped diff. | Scroll-focused Chromium 28/28; final affected Chromium 5/5; source regressions 2/2; scoped ESLint; Prettier; TypeScript and production build passed before the final upstream-only sync; `git diff --check`. The aggregate local Vitest/UI runs were affected by concurrent-worktree resource contention, so exact-head hosted CI remains required before merge. No Supabase/OpenAI/live-provider checks run. | -<<<<<<< HEAD -| 2026-07-17 | PR #713 / codex/chat-workflow-ideas-0916 | b52112df6aa36311d7420189064acd79dcf2c3f5 + reviewed follow-up diff | workflow toolkit review follow-up | Fixed all 14 actionable Codex and CodeRabbit threads: cross-platform path fixtures, installation-managed preflight guidance, complete Supabase-backed API database scoping, per-command approval boundaries, plugin-ignore narrowing, isolated CI-scope proof, remote-Git command guarding, repository-skill verification classification, `TypeError` diagnosis, strict CLI option values, machine-parseable JSON evidence output, and preservation of baseline database/clinical approval gates in the RAG lab. No unresolved actionable finding remains in the reviewed scope. | `npm run verify:cheap` passed with 273 files and 2,599 tests; focused toolkit Vitest 20/20; CI-scope self-test; plugin-ignore proof; `git diff --check`. Exact-head hosted CI remains required after the follow-up push. No Supabase, OpenAI, or other live product-provider command was run. | -======= | 2026-07-17 | codex/chat-audit-remediation-port-20260717 | 4f41093ba01f88e6d04a53f0782e8676806e3f6f | audit-findings remediation and local merge-readiness review | No remaining high-confidence P0-P2 defect in the reviewed diff. The review fixed two integration issues before handoff: transactional delete moved `rag_response_cache` cleanup out of the API route but left the explicit route-table guard stale, and upstream added a migration after the intentionally final fail-closed ACL assertion. The guard now matches direct route queries and the unapplied ACL migration is renumbered last. Publication requires explicit approved manifests, delete/reindex is serialized transactionally with upload compensation, PDF extraction is bounded, search ignores staged generations, and unsafe effective default ACLs block. | Rebasing and regenerated drift manifest against local `origin/main` 220de891; disposable Docker schema replay; publication/delete/ACL SQL fixtures; Python 4/4; focused Vitest 237/237 plus post-sync schema/retrieval 66/66; docs guards; offline RAG 290/290; production-readiness CI ready; `verify:cheap` 2602/2602 before final upstream sync; exact-head `verify:pr-local` formatting, lint, typecheck, 2628/2628 tests, production build (1043 pages), client-secret scan, and RAG fixtures; `git diff --check`. No live Supabase/OpenAI/GitHub/hosted-CI/provider checks, deployment, or live migration apply. | | 2026-07-17 | PR #713 / codex/chat-workflow-ideas-0916 | b52112df6aa36311d7420189064acd79dcf2c3f5 + reviewed follow-up diff | workflow toolkit review follow-up | Fixed all 14 actionable Codex and CodeRabbit threads: cross-platform path fixtures, installation-managed preflight guidance, complete Supabase-backed API database scoping, per-command approval boundaries, plugin-ignore narrowing, isolated CI-scope proof, remote-Git command guarding, repository-skill verification classification, `TypeError` diagnosis, strict CLI option values, machine-parseable JSON evidence output, and preservation of baseline database/clinical approval gates in the RAG lab. No unresolved actionable finding remains in the reviewed scope. | `npm run verify:cheap` passed with 273 files and 2,599 tests; focused toolkit Vitest 20/20; CI-scope self-test; plugin-ignore proof; `git diff --check`. Exact-head hosted CI remains required after the follow-up push. No Supabase, OpenAI, or other live product-provider command was run. | | 2026-07-17 | PR #699 / codex/test-reliability-hardening | 6202835ab1cf3703af311b9afdf372f74c63e040 | branch-cleanup-superseded | Closed as fully superseded by merged PR #705 (`e5caaa46c`). Range-diff maps the original implementation commit to #705's first integration commit; #705 then adds seven focused reliability fixes, while #699's remaining commit is merge-only and contributes no unique relevant patch. The exact-SHA remote ref and the unregistered local predecessor ref (`b518c1de9`) were deleted after final rechecks. | Fresh GitHub PR/head/status inventory; exact `ls-remote` and local-ref checks; cherry-pick-aware log; range-diff against PR #705's merged head; merge-parent verification; exact leased remote deletion and exact-old-value local `update-ref` deletion. No Supabase/OpenAI/product-provider checks run. | @@ -603,4 +600,3 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-15 | HEAD detached 570e6ba56 + WIP tree | 570e6ba56ae60bea56a32801b9cc96c5a8dfde4f | thorough multi-lens review: WIP RAG/schema + clinical design/UI + architecture/bug-hunt | Changes requested: no P0. Confirmed P1s in WIP � registryCorpusDetailHref typecheck break; ChunkLoadCache error/null poisoning across parallel hydrations; registry cleanup `::uuid` cast abort; corrector GIN unused by query path; new table-facts trgm index expression mismatch vs trgm_matches. Design: production clinical shell stays token/a11y-aligned; favourites nav multi-gradient bars and mockup hex drift fight clinical density. Residual: concurrent cache race, SECURITY DEFINER revoke gaps, schema/migration lifecycle drift, accidental pnpm-lock.yaml. | `npm run typecheck` (red: registry link callers + stale .next apps types); static SQL/expr/diff review; architecture + bug-hunt agents; design-system grep (tokens, reduced-motion, forced-colors). Not run: vitest, verify:*, ensure/browser screenshots, live Supabase/OpenAI. frontend-ui-reviewer subagent blocked by usage limit � design pass done inline. | | 2026-07-18 | claude/clinical-kb-pwa-review-asi3wb (PR #826) | 3d9ee5f44dea9edb1ef5af28f5f265d88d8b9f29 | PWA hardening implementation (plan Phase 1) | Implemented the three open findings from the 2026-07-17 PWA setup review with zero cache-semantics change: committed the rule-6 retirement worker `public/sw-kill-switch.js` with a five-test lock (`tests/pwa-kill-switch.test.ts`), bound the `offline.html` sha256 to the sw.js `CACHE_VERSION` pairing in `tests/pwa-manifest.test.ts` (drift trap closed), added the `?pwa-dev=0` local teardown to `pwa-lifecycle.tsx` with a dom test proving foreign workers and caches stay untouched, and updated `docs/pwa.md` rules 1 and 6 plus the local-dev cleanup step. Phase 0 of the approved plan (pr-policy `base_ref` checkout fix + the Set-Cookie worker-test case) was found already merged to main and skipped. | Focused Vitest 53/53. `verify:cheap` and the `verify:pr-local` unit stage green except `tests/pdf-extraction-budget.test.ts`, which fails identically on clean main in this container (child-process semantics; baselined twice). `verify:ui` 218 passed with 2 container-baselined pre-existing failures: the `ui-pwa` installability test (Chromium `in-incognito` artifact, reproduced from a clean-main detached worktree with its own server) and the `ui-smoke` document-viewer PDF-canvas mobile test (also fails on clean main `54229f0`; flagged as possible upstream regression). `format:check` clean for repo files. Conditional build/bundle stages deferred to the blocking hosted CI Build job on PR #826. No provider-backed checks run. | | 2026-07-18 | claude/clinical-kb-pwa-review-asi3wb (PR #835) | d46f381ac27b53b1bd5ac0ef77962fbd48cf3aa7 | PWA manifest and install-UX polish (plan Phase 2) | Implemented Phase 2 of the PWA plan with cache semantics untouched: `launch_handler` navigate-existing/auto and `display_override` standalone/minimal-ui in `manifest.ts`; `monochrome-192/512` icon variants rendered as a white alpha-only silhouette from the shared brand mark via `BRAND_MONOCHROME`; a one-time iOS/iPadOS Add to Home Screen hint in `pwa-lifecycle.tsx` (30-day dismissal key, never in standalone, timer-deferred eligibility for the set-state-in-effect lint rule). Docs Installability section updated; manifest screenshots remain deferred per the production-capture precondition. Phase 2 re-scout confirmed zero upstream drift before implementation. | Focused Vitest 55/55. `verify:cheap` 2773 passed/1 failed and `verify:pr-local` unit stage identical — the lone failure is the known container-only `pdf-extraction-budget` artifact (clean-main baselined; hosted CI green on #826). `test:e2e:pwa`: privacy journey passed; icon probes validated both new monochrome PNGs; sole installability error remains the container `in-incognito` artifact. `verify:ui` 219 passed/2 failed — exactly the two clean-main-baselined container artifacts, no new failures. `format:check` clean. Build/bundle stages deferred to the blocking hosted CI Build job on PR #835. No provider-backed checks run. | ->>>>>>> origin/main diff --git a/scripts/ci-change-scope.mjs b/scripts/ci-change-scope.mjs index 9e9805681..8c7383707 100644 --- a/scripts/ci-change-scope.mjs +++ b/scripts/ci-change-scope.mjs @@ -73,11 +73,7 @@ const workflowPatterns = [ "AGENTS.md", "docs/codex-review-protocol.md", "docs/process-hardening.md", -<<<<<<< HEAD - /^scripts\/(?:ci-change-scope|verify-pr-local|eval-rag-offline|check-github-action-pins|check-codex-autofix-workflow|productivity-core|productivity-workflow|external-workflow)\.mjs$/, -======= /^scripts\/(?:ci-change-scope|ci-triage|pr-policy|verify-pr-local|eval-rag-offline|check-github-action-pins|check-codex-autofix-workflow|productivity-core|productivity-workflow|external-workflow)\.mjs$/, ->>>>>>> origin/main ]; const codexAutofixPatterns = [