Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
af82a71
Integrate desktop authoring line into core (squash of feature/authori…
mattcfilbert Jul 9, 2026
96a3b14
Resync feature/authoring → feature/desktop (one-time content-level re…
mattcfilbert Jul 15, 2026
d2ace79
feat(combined): TOOL_PROFILE=combined-lean — full desktop + lazy web …
mattcfilbert Jul 15, 2026
67e88a5
fix(metadata): detail→lod normalization + SQL string-fn knowledge + f…
mattcfilbert Jul 15, 2026
a343ddc
feat(classify): semantic-role-first geo binding + masking hardening (…
mattcfilbert Jul 15, 2026
4ee1239
Port a2td overnight portability+reliability wave (W1/W2/W3/W4/W6/W9) …
mattcfilbert Jul 15, 2026
27ab2c0
W65 red-team fixes: sidecar stamping, geo concept check, readback hon…
mattcfilbert Jul 15, 2026
1a4480d
fix(binder): CLS-003 — temporal completion won't swallow an ask-named…
mattcfilbert Jul 15, 2026
99090d3
Port a2td #209 GUS dogfood wave → feature/desktop (W-23447478/507/473…
mattcfilbert Jul 16, 2026
284d886
feat(interaction): ask-user clarification tool + fail-closed ambiguit…
mattcfilbert Jul 16, 2026
a1658f4
fix(desktop): canonical-name gate — goto-sheet can never focus a stal…
mattcfilbert Jul 16, 2026
5d28364
port(a2td #214): refine sort-insert on unsorted simple bars (#523)
mattcfilbert Jul 16, 2026
ccd809f
port(a2td #215): compose-focus suppression (parallel builds stop stea…
mattcfilbert Jul 16, 2026
a539691
port(a2td #213): resolve-field self-heals stale workbook caches (#526)
mattcfilbert Jul 16, 2026
34f79c0
knowledge: failure-recovery-honesty module (on-demand twin of the a2t…
mattcfilbert Jul 16, 2026
625b699
feat(desktop): route-table gates for authoring skill + plan-before-bu…
tableaukyler Jul 16, 2026
1ed6b77
build(desktop): add watch mode for fast rebuild loop (#536)
lauren-jacksonSFDC Jul 17, 2026
58ff4a3
feat(desktop): propose/classify field identity — a2td W2c lockstep tw…
mattcfilbert Jul 17, 2026
f2c8258
fix(desktop): promised-sort readback loss fails the verification rece…
mattcfilbert Jul 17, 2026
a2a0632
feat(desktop): exact column_ref first-class + shared ref helpers (a2t…
mattcfilbert Jul 17, 2026
f4dfe45
feat(desktop): planner datasource-qualified fields + apply-side datas…
mattcfilbert Jul 17, 2026
d5a3777
feat(desktop): known-command guard on execute-tableau-command + Notio…
mattcfilbert Jul 19, 2026
987e2cd
docs(knowledge): NotionalSpec semantic loop — live-verified authoring…
mattcfilbert Jul 19, 2026
b66941e
feat(desktop): episode-lite telemetry — the eval loop's product-path …
mattcfilbert Jul 19, 2026
af8c694
feat(desktop): TOOL_PROFILE=spec-loop — the 5-tool semantic-first sur…
mattcfilbert Jul 19, 2026
3a80ce9
fix(desktop): carry ancestor xmlns declarations onto extracted sheet/…
mattcfilbert Jul 19, 2026
b8580d7
docs(knowledge): wrong-fork redirects to the semantic calc twin + con…
mattcfilbert Jul 19, 2026
073ee2b
fix(desktop): search-commands no-match recommendation steers semantic…
mattcfilbert Jul 19, 2026
814e0e7
feat(desktop): pre-dispatch guard for generate-viz-from-notional-spec…
mattcfilbert Jul 19, 2026
ba357d7
fix(desktop): refine-worksheet polls its confirmation readback (async…
mattcfilbert Jul 19, 2026
ad185f2
feat(desktop): param-contract guard for every command + live-verified…
mattcfilbert Jul 19, 2026
1f836b6
fix(desktop): verify applied sheet exists before goto-sheet (modal 47…
mattcfilbert Jul 19, 2026
9e64705
desktop: surface the document round-trip (discoverability) + load con…
mattcfilbert Jul 20, 2026
5c5b328
feat(desktop): cut model round-trips — bind-template fast-path on the…
mattcfilbert Jul 20, 2026
41f059e
fix(desktop): pin External Client API schemas to the captured 0.1.0 o…
mattcfilbert Jul 20, 2026
ca66860
desktop: calc-ask routing via author verbs + NotionalSpec corpus purg…
mattcfilbert Jul 21, 2026
4d7afe5
fix(desktop): teach the known-live-500 fallback on tabdoc:sort-nested…
mattcfilbert Jul 21, 2026
0c40c65
feat(desktop): waterfall anchor_category slot + sort proposal wins ov…
mattcfilbert Jul 21, 2026
6961718
feat(desktop): waterfall bind hint names the step-order column (#562)
mattcfilbert Jul 21, 2026
58dc7b2
docs(knowledge): waterfall step-order guidance + profitability margin…
mattcfilbert Jul 21, 2026
1957c6a
feat(desktop): temporal_axis_from_string — bind a string month to a c…
mattcfilbert Jul 21, 2026
620fde2
fix(ci): widen vitest teardown window to stop flaky onTaskUpdate RPC …
mattcfilbert Jul 21, 2026
43e695c
feat(desktop): search-knowledge tool — targeted top-N over list-all (…
mattcfilbert Jul 21, 2026
f3b0b21
fix(desktop): honor session pin when stamping cache sidecars (#568)
tableaukyler Jul 21, 2026
684b314
docs: insights → bind-template mapping (Pulse quick-viz pointer) (#569)
mattcfilbert Jul 21, 2026
3d75d01
Resync main → feature/desktop (brings #502 insight tools + resolve-da…
mattcfilbert Jul 21, 2026
bd9234b
feat(web): slim param on generate-pulse-metric-value-insight-bundle (…
nawartamawi Jul 21, 2026
3a44b6a
feat(fields): add verbosity=slim to list-available-fields (#535)
nawartamawi Jul 21, 2026
1d97073
feat: env-gated External-API command-registry guard (wire names, enum…
mattcfilbert Jul 21, 2026
96aedb9
feat: data-first tools — get-summary-data + list-site-datasources (#573)
mattcfilbert Jul 21, 2026
3e6379b
Resync main → feature/desktop (id_type, MCP-Apps folders, stale-conte…
mattcfilbert Jul 21, 2026
e48d784
feat: surface invokeCommand output — consume the 0.1.1 result envelop…
mattcfilbert Jul 21, 2026
4d461ee
refactor: converge slim vocabulary — verbosity enum on the Pulse bund…
mattcfilbert Jul 21, 2026
0c2d8fe
chore: scrub internal references; retire NotionalSpec readback + inst…
mattcfilbert Jul 21, 2026
5f96962
fix: enum linkage accepts tuple-shaped type_of_param (the shipped reg…
mattcfilbert Jul 21, 2026
8ffbc5b
purge: dead hack-build command dialect — data + knowledge (part A) (#…
mattcfilbert Jul 21, 2026
b798461
refactor: architectural consolidation — guard extraction, read harnes…
mattcfilbert Jul 21, 2026
137dabc
feat: knowledge search length-router — long queries become first-clas…
mattcfilbert Jul 21, 2026
7cdef4a
feat: regrow the singable surface — shelf placement + dashboard compo…
mattcfilbert Jul 21, 2026
4727c3a
feat(binder): three graces + 30-tool singable surface — optional geo …
mattcfilbert Jul 22, 2026
ffa3833
feat(binder): bind-template target_worksheet — one-call render onto t…
mattcfilbert Jul 22, 2026
ff1a1e4
fix(knowledge): pin served docs to the tool registry; purge retired t…
mattcfilbert Jul 22, 2026
9cd8243
test: next-move law — emitted guidance may only name registered tools…
mattcfilbert Jul 22, 2026
50446b9
fix(binder): terminal signal on complete auto-apply bind (Blake spira…
mattcfilbert Jul 22, 2026
3fe9e29
feat(binder): measure-free lat/long symbol map — confident coordinate…
mattcfilbert Jul 22, 2026
993667b
fix(binder): classify honors temporal_from_string — string month bind…
mattcfilbert Jul 22, 2026
9e38d63
fix(binder): two demo-blockers — session:'default' sentinel + build-a…
mattcfilbert Jul 22, 2026
1d5b53d
fix(binder): latlon resolver binds WIDE real map schemas (best detail…
mattcfilbert Jul 22, 2026
95edd00
fix(binder): honest bind-template annotations — not read-only, not id…
mattcfilbert Jul 22, 2026
a2cb446
fix(binder): federated-duplicate tie-break — confident-bind survives …
mattcfilbert Jul 22, 2026
60fbf25
feat(desktop): atomic activate-sheet tool (goto-sheet recovery) — rep…
mattcfilbert Jul 22, 2026
58914c5
fix(binder): live map ask confident-binds — cross-base tie + decline-…
mattcfilbert Jul 22, 2026
b3a80ed
perf(tools): bind-template advertises self-discovery — kill the waste…
mattcfilbert Jul 22, 2026
11bbcc4
fix: read single sheets/dashboards as fragments and apply them withou…
lauren-jacksonSFDC Jul 22, 2026
2e86a2f
feat(binder): bind-recovery state machine — thrash protection for the…
mattcfilbert Jul 22, 2026
30f9edf
fix(apply): a sheet always ships with its window — kill the unreachab…
mattcfilbert Jul 22, 2026
12ae10e
test(binder): pin bind-template's content-block envelope (#605)
mattcfilbert Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
17 changes: 13 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
branches:
- main
- feature/desktop

jobs:
build:
Expand All @@ -13,16 +14,16 @@ jobs:
matrix:
node-version: ['>=22.7.5 <23', '24.x']
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Set up Docker
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4

- name: Install dependencies
run: npm ci
Expand All @@ -36,6 +37,14 @@ jobs:
- name: Build
run: npm run build

# --dirty: the plain build:desktop CLEANS ./build first, deleting the
# build/index.js the default build just produced — the e2e suites spawn
# `node build/index.js` children, which then die pre-handshake
# ("Connection closed") until server.test.ts happens to rebuild it
# mid-pool. Root-caused 2026-07-15 on the resync PR (#504).
- name: Build desktop variant
run: npm run build:desktop:dirty

- name: Build Docker image
run: npm run build:docker

Expand Down Expand Up @@ -115,7 +124,7 @@ jobs:

- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: artifacts-node${{ matrix.node-version == '>=22.7.5 <23' && '22.x' || matrix.node-version }}
if-no-files-found: error
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cleanup-releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v8
- uses: actions/github-script@v9
with:
script: |
// Get all releases with pagination, sorted by creation date (newest first)
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
name: Build Docusaurus
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 20.x
cache: npm
Expand All @@ -31,7 +31,7 @@ jobs:
run: npm run build

- name: Upload Build Artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v5
with:
path: docs/build/tableau-mcp

Expand All @@ -53,4 +53,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
33 changes: 27 additions & 6 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,52 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Preflight — release tag must match package.json version
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
# The `latest` guard below keys off the release TAG NAME while the npm
# side (publish.yml) keys off package.json — this preflight (same check
# in both workflows) makes a mismatch fail loudly instead of letting the
# two registries disagree about moving `latest`.
run: |
VERSION="$(node -p "require('./package.json').version")"
if [[ -z "$VERSION" || "$VERSION" == "undefined" ]]; then
echo "::error::Could not read version from package.json — aborting"
exit 1
fi
if [[ "$RELEASE_TAG" != "v$VERSION" ]]; then
echo "::error::Release tag '$RELEASE_TAG' != 'v$VERSION' (package.json) — aborting. Tag the exact commit whose package.json matches, or fix the release tag."
exit 1
fi

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
# Move `latest` only for STABLE releases — a prerelease/hotfix tag
# (one containing a `-`, e.g. v2.18.0-hotfix.1) gets its exact-version
# Docker tag but must NOT steal `latest`, mirroring the npm publish guard.
type=raw,value=latest,enable=${{ github.event_name == 'release' && !contains(github.event.release.tag_name, '-') }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
push: true
Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/pr-feature-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: PR Feature Label

on:
pull_request:
types: [opened, edited, synchronize, reopened]

jobs:
label:
runs-on: ubuntu-latest
# Skip fork PRs (read-only token can't label) and dependabot — matches repo convention
if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]'
permissions:
issues: write # create the label if it doesn't exist yet
pull-requests: write # add the label to the PR
steps:
- uses: actions/github-script@v8
with:
script: |
const prefix = 'feature/';
const pr = context.payload.pull_request;

// Label whichever side of the PR is a feature branch:
// - base is feature/* : a PR merging INTO a long-lived feature branch
// - head is feature/* : a feature branch being merged into main (or elsewhere)
// Both produce a "feature:<name>" label; if both sides qualify, both are applied.
const labels = [...new Set(
[pr.base.ref, pr.head.ref]
.filter((ref) => ref.startsWith(prefix))
.map((ref) => `feature:${ref.slice(prefix.length)}`)
)];

if (labels.length === 0) {
console.log(`Neither base "${pr.base.ref}" nor head "${pr.head.ref}" is a feature branch; nothing to do.`);
return;
}

// Create any labels that don't exist yet (auto-created gray).
for (const label of labels) {
try {
await github.rest.issues.getLabel({ ...context.repo, name: label });
} catch (error) {
if (error.status === 404) {
await github.rest.issues.createLabel({ ...context.repo, name: label, color: 'ededed' });
console.log(`Created label "${label}"`);
} else {
throw error;
}
}
}

await github.rest.issues.addLabels({
...context.repo,
issue_number: pr.number,
labels,
});
console.log(`Applied label(s) ${labels.map((l) => `"${l}"`).join(', ')} to PR #${pr.number}`);
4 changes: 2 additions & 2 deletions .github/workflows/pr-post-merge-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: '>=22.7.5 <23'
cache: 'npm'
Expand Down
43 changes: 41 additions & 2 deletions .github/workflows/pr-title-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,58 @@ on:
pull_request:
branches:
- main
types: [opened, edited, synchronize, reopened]
types: [opened, edited, synchronize, reopened, labeled, unlabeled]

jobs:
check-title:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Check PR title format
- name: Ensure skip-title-check label exists
if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]'
uses: actions/github-script@v7
with:
script: |
const name = 'skip-title-check';
try {
await github.rest.issues.getLabel({ ...context.repo, name });
} catch (error) {
if (error.status === 404) {
try {
await github.rest.issues.createLabel({
...context.repo,
name,
color: 'ededed',
description: 'Bypass the PR title @W- work item check',
});
} catch (createError) {
// 422 = already exists (e.g., concurrent workflow run)
if (createError.status !== 422) {
throw createError;
}
}
} else {
throw error;
}
}

- name: Check PR title format
if: >-
github.event.pull_request.head.repo.full_name == github.repository &&
github.actor != 'dependabot[bot]' &&
!contains(github.event.pull_request.labels.*.name, 'skip-title-check')
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ "${PR_TITLE,,}" =~ ^docs: ]]; then
echo "✅ Docs PR - skipping work item number check"
exit 0
fi

if [[ ! "$PR_TITLE" =~ ^@W-[0-9]+ ]]; then
echo "❌ PR title must match the pattern '@W-<digits>' (e.g., @W-123456)"
echo " To bypass: prefix the title with 'docs:' or add the 'skip-title-check' label."
exit 1
fi

Expand Down
58 changes: 51 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,69 @@ jobs:
contents: read
id-token: write # Needed for OIDC authentication
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: '>=22.7.5'
registry-url: https://registry.npmjs.org/
- run: |
- env:
# Passed via env (not inline ${{ }} in the script) — the tag name is
# release-author-controlled input; env keeps it out of shell parsing.
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
git fetch --tags
git checkout -b publish ${{ github.event.release.tag_name }}
git checkout -b publish "$RELEASE_TAG"
npm install -g npm@latest
npm ci
npm run build
npm publish
# Build the desktop variant into the same build/ dir. --dirty preserves the
# default build/index.js emitted above (a non-dirty desktop build would wipe it).
npx tsx src/scripts/build.ts --variant desktop --dirty
# Fail the publish if either entry point or the staged desktop data is missing.
test -f build/index.js
test -f build/index.desktop.js
test -d build/desktop/data
# Prerelease-aware publish. A SemVer prerelease version (one containing a
# `-`, e.g. 2.18.0-hotfix.1; build metadata `+...` is NOT a prerelease)
# must NOT reassign the `latest` npm dist-tag — otherwise a hotfix off an
# old release line would steal `latest` from the real release line. npm
# moves `latest` on EVERY bare `npm publish`, so prereleases publish
# under a dedicated dist-tag instead.
# NOTE: the dist-tag must NOT itself be a valid SemVer version (npm
# rejects that), so we use `hotfix-v<version>` rather than the bare
# version. Consumers pin the exact version (`@2.18.0-hotfix.1`)
# regardless; the dist-tag is just a non-`latest` pointer.
# Fail CLOSED: if the version can't be read, abort before publishing —
# a bare publish on an empty version would wrongly move `latest`.
# VERSION comes from package.json (the repo's own committed value, not
# external input) into a shell var — no GitHub expression interpolation here.
VERSION="$(node -p "require('./package.json').version")"
if [[ -z "$VERSION" || "$VERSION" == "undefined" ]]; then
echo "::error::Could not read version from package.json — aborting publish (refusing to risk moving 'latest')"
exit 1
fi
# PREFLIGHT: the release tag must be exactly v<package.json version>.
# npm decides prerelease-vs-stable from package.json while Docker
# (docker-publish.yml) decides from the release tag name — a mismatch
# would let the two registries disagree about moving `latest`.
if [[ "$RELEASE_TAG" != "v$VERSION" ]]; then
echo "::error::Release tag '$RELEASE_TAG' != 'v$VERSION' (package.json) — aborting. Tag the exact commit whose package.json matches, or fix the release tag."
exit 1
fi
if [[ "$VERSION" == *-* ]]; then
echo "Prerelease $VERSION -> npm publish --tag hotfix-v$VERSION (NOT moving 'latest')"
npm publish --tag "hotfix-v$VERSION"
else
echo "Stable $VERSION -> npm publish (moves 'latest')"
npm publish
fi
upload-mcpb:
runs-on: ubuntu-latest
permissions:
contents: write # Needed to upload release assets
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: '>=22.7.5'
cache: 'npm'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v7

- name: Create tag
id: tag
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
name: Test deployment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 20.x
cache: npm
Expand All @@ -30,7 +30,7 @@ jobs:
- name: Test build website
run: npm run build
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: artifacts
if-no-files-found: error
Expand Down
Loading