Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8573fc8
feat(pkg-py): add interactive artifact generation
cpsievert Aug 19, 2026
77f6724
refactor: persist artifacts through chat history
cpsievert Aug 19, 2026
36ce2b6
test: update artifact persistence expectations
cpsievert Aug 19, 2026
f688b50
Simplify artifact revision state
cpsievert Aug 19, 2026
254c72d
Merge branch 'feat/artifact-latest-only' into feat/artifact-feature
cpsievert Aug 20, 2026
7c59b6f
refactor(pkg-py): remove redundant artifact state
cpsievert Aug 20, 2026
16e1dfd
refactor(pkg-py): require artifact language selection
cpsievert Aug 20, 2026
2bb13fb
test(pkg-py): remove exploratory artifact browser suite
cpsievert Aug 20, 2026
2d0e3bf
perf(pkg-py): stream artifact source deltas
cpsievert Aug 20, 2026
e034829
fix(pkg-py): default artifact language to Python
cpsievert Aug 20, 2026
c7c8b18
test(pkg-py): simplify artifact coverage
cpsievert Aug 20, 2026
d0217ab
refactor: rename artifact feature to handoff
cpsievert Aug 20, 2026
d154258
fix(handoff): preserve SQL source fidelity
cpsievert Aug 20, 2026
7f3c85a
fix(pkg-py): handle non-finite gallery floats
cpsievert Aug 20, 2026
2900fa3
fix(handoff): preserve bundles on generation failure
cpsievert Aug 20, 2026
bccdac7
fix(handoff): enable downloads after commit
cpsievert Aug 20, 2026
98c78c9
fix(py): package handoff language icons
cpsievert Aug 20, 2026
49f5019
fix(handoff): ignore post-commit view failure
cpsievert Aug 20, 2026
1fe3392
fix(py): normalize R handoff icon line endings
cpsievert Aug 20, 2026
9ed3709
ci: cover all shared asset paths
cpsievert Aug 20, 2026
6a8d17f
ci: align shared asset filters
cpsievert Aug 20, 2026
8de0747
refactor(py): replace handoff tool with prompt guidance
cpsievert Aug 20, 2026
7a1bbec
refactor(py): remove obsolete handoff callback
cpsievert Aug 20, 2026
0a25e50
fix(handoff): persist metadata in Shiny bookmarks
cpsievert Aug 20, 2026
99e5afe
fix(handoff): clarify state restore errors
cpsievert Aug 20, 2026
4c04fb6
feat(handoff): externalize oversized dataframe bundles
cpsievert Aug 20, 2026
7cf7d41
fix(handoff): clarify external data requirements
cpsievert Aug 20, 2026
85de2e6
feat(handoff): define external data correction prompt
cpsievert Aug 20, 2026
3a5c7ba
test(handoff): strengthen correction prompt contract
cpsievert Aug 20, 2026
ad5def0
fix(handoff): harden external correction prompt
cpsievert Aug 20, 2026
30b7359
feat(handoff): repair oversized data access
cpsievert Aug 20, 2026
0618960
fix(handoff): normalize correction contract errors
cpsievert Aug 20, 2026
c987894
fix(handoff): preserve unrelated correction errors
cpsievert Aug 20, 2026
6103a3d
fix(handoff): make correction transactions cancellation-safe
cpsievert Aug 20, 2026
c3482de
test(handoff): add required language to module fixture
cpsievert Aug 20, 2026
9649fc3
chore: preserve handoff UI updates
cpsievert Aug 20, 2026
97dea4d
test(py): isolate handoff Shiny servers
cpsievert Aug 20, 2026
91e1a2d
fix(handoff): preserve export failures on overflow
cpsievert Aug 21, 2026
49d22cf
feat(r): add the /handoff workflow
cpsievert Aug 21, 2026
cce2ecb
Merge feat/handoff-r-port: bring the /handoff workflow to R
cpsievert Aug 25, 2026
52d1d9e
fix(r): address handoff port merge review findings
cpsievert Aug 25, 2026
0418da3
Merge remote-tracking branch 'origin/main' into fix/handoff-json-tool…
cpsievert Aug 25, 2026
a57f656
fix(r): adapt handoff gallery to JSON-string query tool values
cpsievert Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg-r/tests/testthat/_snaps/*.md whitespace=-blank-at-eof
36 changes: 35 additions & 1 deletion .github/workflows/js-check.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
name: Check - Shared Viz Assets
name: Check - Shared Web Assets

on:
workflow_dispatch:
push:
branches: ["main", "rc-*"]
paths:
- "js/**"
- "pkg-py/src/querychat/static/css/handoff.css"
- "pkg-py/src/querychat/static/css/viz.css"
- "pkg-py/src/querychat/static/img/handoff-language-python.svg"
- "pkg-py/src/querychat/static/img/handoff-language-r.svg"
- "pkg-py/src/querychat/static/js/handoff.js"
- "pkg-py/src/querychat/static/js/schema-display.js"
- "pkg-py/src/querychat/static/js/viz.js"
- "pkg-r/inst/htmldep/handoff.css"
- "pkg-r/inst/htmldep/handoff.js"
- "pkg-r/inst/htmldep/img/handoff-language-python.svg"
- "pkg-r/inst/htmldep/img/handoff-language-r.svg"
- "pkg-r/inst/htmldep/schema-display.js"
- "pkg-r/inst/htmldep/viz.css"
- "pkg-r/inst/htmldep/viz.js"
- "shared/handoff-formats.yml"
- "shared/img/handoff-language-python.svg"
- "shared/img/handoff-language-r.svg"
- "pkg-py/src/querychat/handoff-formats.yml"
- "pkg-r/inst/handoff-formats.yml"
- "Makefile"
- ".github/workflows/js-check.yml"
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "js/**"
- "pkg-py/src/querychat/static/css/handoff.css"
- "pkg-py/src/querychat/static/css/viz.css"
- "pkg-py/src/querychat/static/img/handoff-language-python.svg"
- "pkg-py/src/querychat/static/img/handoff-language-r.svg"
- "pkg-py/src/querychat/static/js/handoff.js"
- "pkg-py/src/querychat/static/js/schema-display.js"
- "pkg-py/src/querychat/static/js/viz.js"
- "pkg-r/inst/htmldep/handoff.css"
- "pkg-r/inst/htmldep/handoff.js"
- "pkg-r/inst/htmldep/img/handoff-language-python.svg"
- "pkg-r/inst/htmldep/img/handoff-language-r.svg"
- "pkg-r/inst/htmldep/schema-display.js"
- "pkg-r/inst/htmldep/viz.css"
- "pkg-r/inst/htmldep/viz.js"
- "shared/handoff-formats.yml"
- "shared/img/handoff-language-python.svg"
- "shared/img/handoff-language-r.svg"
- "pkg-py/src/querychat/handoff-formats.yml"
- "pkg-r/inst/handoff-formats.yml"
- "Makefile"
- ".github/workflows/js-check.yml"

Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ The package has deprecated the old functional API (`querychat_init()`, `querycha
1. Always test changes with both R and Python implementations to maintain consistency
2. Use the provided Make commands for development tasks
3. Follow the existing code style (ruff for Python, `air format .` for R)
4. Ask before running tests (the user may want to run them themselves)
4. Run normal local tests and checks autonomously. Ask first only for destructive,
external, or unusually expensive commands.
5. Update documentation when adding new features
6. Always ask about file names before writing any new code
7. Always pay attention to your working directory when running commands, especially when working in a sub-package.
Expand Down
73 changes: 68 additions & 5 deletions js/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ const jsTargets = [
source: "src/viz.ts",
output: "../pkg-r/inst/htmldep/viz.js",
},
{
source: "src/handoff.ts",
output: "../pkg-py/src/querychat/static/js/handoff.js",
},
{
source: "src/handoff.ts",
output: "../pkg-r/inst/htmldep/handoff.js",
},
{
source: "src/schema-display.js",
output: "../pkg-py/src/querychat/static/js/schema-display.js",
Expand All @@ -43,6 +51,43 @@ const cssTargets = [
source: "src/viz.css",
output: "../pkg-r/inst/htmldep/viz.css",
},
{
source: "src/handoff.css",
output: "../pkg-py/src/querychat/static/css/handoff.css",
},
{
source: "src/handoff.css",
output: "../pkg-r/inst/htmldep/handoff.css",
transform: (source) =>
source.replaceAll("../img/handoff-language-", "img/handoff-language-"),
},
];

const rawTargets = [
{
source: "../shared/img/handoff-language-python.svg",
output: "../pkg-py/src/querychat/static/img/handoff-language-python.svg",
},
{
source: "../shared/img/handoff-language-python.svg",
output: "../pkg-r/inst/htmldep/img/handoff-language-python.svg",
},
{
source: "../shared/img/handoff-language-r.svg",
output: "../pkg-py/src/querychat/static/img/handoff-language-r.svg",
},
{
source: "../shared/img/handoff-language-r.svg",
output: "../pkg-r/inst/htmldep/img/handoff-language-r.svg",
},
{
source: "../shared/handoff-formats.yml",
output: "../pkg-py/src/querychat/handoff-formats.yml",
},
{
source: "../shared/handoff-formats.yml",
output: "../pkg-r/inst/handoff-formats.yml",
},
];

const ensureParentDir = async (relativePath) => {
Expand All @@ -51,7 +96,7 @@ const ensureParentDir = async (relativePath) => {
return absolutePath;
};

export const assetTargets = [...cssTargets, ...jsTargets];
export const assetTargets = [...cssTargets, ...jsTargets, ...rawTargets];

export const resolveOutputPath = (baseDir, relativePath) =>
path.resolve(baseDir, path.relative(repoDir, path.resolve(rootDir, relativePath)));
Expand All @@ -65,10 +110,11 @@ const findMissingSources = async (targets) => {
const missingSources = [];

for (const source of uniqueSources(targets)) {
const sourcePath = path.resolve(rootDir, source);
try {
await access(path.resolve(rootDir, source));
await access(sourcePath);
} catch {
missingSources.push(`js/${source}`);
missingSources.push(path.relative(repoDir, sourcePath));
}
}

Expand All @@ -78,8 +124,13 @@ const findMissingSources = async (targets) => {
const reportMissingSources = async () => {
const missingCssSources = await findMissingSources(cssTargets);
const missingJsSources = await findMissingSources(jsTargets);
const missingRawSources = await findMissingSources(rawTargets);

if (missingCssSources.length === 0 && missingJsSources.length === 0) {
if (
missingCssSources.length === 0 &&
missingJsSources.length === 0 &&
missingRawSources.length === 0
) {
return;
}

Expand All @@ -93,6 +144,10 @@ const reportMissingSources = async () => {
messages.push(`Missing JS source files:\n- ${missingJsSources.join("\n- ")}`);
}

if (missingRawSources.length > 0) {
messages.push(`Missing raw source files:\n- ${missingRawSources.join("\n- ")}`);
}

throw new Error(messages.join("\n\n"));
};

Expand All @@ -101,8 +156,9 @@ export const stageBuildOutputs = async (stageDir) => {
const cssSourcePath = path.resolve(rootDir, target.source);
const cssSource = await readFile(cssSourcePath, "utf8");
const outputPath = resolveOutputPath(stageDir, target.output);
const outputSource = target.transform ? target.transform(cssSource) : cssSource;
await mkdir(path.dirname(outputPath), { recursive: true });
await writeFile(outputPath, `${banner(target.source)}${cssSource}`, "utf8");
await writeFile(outputPath, `${banner(target.source)}${outputSource}`, "utf8");
}

for (const target of jsTargets) {
Expand All @@ -121,6 +177,13 @@ export const stageBuildOutputs = async (stageDir) => {
},
});
}

for (const target of rawTargets) {
const sourcePath = path.resolve(rootDir, target.source);
const outputPath = resolveOutputPath(stageDir, target.output);
await mkdir(path.dirname(outputPath), { recursive: true });
await copyFile(sourcePath, outputPath);
}
};

export const commitBuildOutputs = async (stageDir) => {
Expand Down
2 changes: 1 addition & 1 deletion js/check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ await withStagedBuild(async (stageDir) => {
});

if (staleOutputs.length > 0) {
console.error("Generated web assets are out of sync. Run `make js-build`.");
console.error("Generated shared assets are out of sync. Run `make js-build`.");
for (const outputPath of staleOutputs) {
console.error(`- ${outputPath}`);
}
Expand Down
Loading
Loading