The TUI is neither published nor bundled into the qqq binaries yet, but
it builds on the same kernel, so a release that breaks it should stop.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings in the TUI package and the hardening review (createProgram in
program.ts, qqq doctor, the golden-output suite). Resolutions: main.ts
from the base; program.ts prints versionText(), whose second line now
starts with "UI API client <version>" as the golden test expects and
adds the supported server range; the CLI package keeps the release
metadata with the base's exports and its new conformance dependency,
and conformance goes to 1.6.0-rc.2 because the CLI now ships against it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings in the TUI stream (program.ts with createProgram and runCli, the io
object, qqq ui, session/login, local-state, filter completion, the result
cursor, the file navigator and env/doctor). Resolutions:
- main.ts is the base's thin entry; it still awaits the 0.1 credentials import.
- program.ts carries the option fixes (--version only, --server without -s)
and registers qqq doctor.
- One set of ui helpers: the TUI's. readUiSettings/writeUiSettings are gone;
qqq config set|get download-dir, upload-dir and download-mode use
readFilePreferences/writeFilePreferences (ui.files), and the new
qqq config set|get theme uses readTheme/writeTheme. readConfig keeps the
corrupt-file refusal and unknown sections; the profiles test covers files,
theme and an unknown section together.
- qqq doctor's terminal rows come from checkEnvironment, with formatReport
under the table; --fix asks before each applyLiveFix and calls
appendTmuxConf only after a y typed at a terminal.
- runCli and the prompts coexist (stdin is not a terminal in runCli, so
prompts refuse; direct stdout writes are captured), tested in a child process.
- @qrun-io/qqq-ui-api-conformance is a dependency of the CLI package.
- The parity matrix is regenerated; docs/review-2026-10-31.md records each
merge decision.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/review-2026-10-31.md lists each finding of the fresh-eyes review, its
severity and the fix (or why it was left). TRY-IT.md was run block by block
against the sample server and corrected: placeholders written as commands,
missing upload files, record ids the shared data no longer matches; it now
documents the option names shared by every noun, the footer, the exit codes
for a no, Ctrl-C and gateways, the download directory and qqq doctor. The
parity matrix notes REC-022 (BLOB upload and download) as verified and the
REVEAL masking in lists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/install.sh picks the binary for the machine (musl on Alpine,
native arm64 under Rosetta), verifies it against the release's
SHA256SUMS and installs to ~/.local/bin. qqq.rb and qqq.json follow the
qctl tap and bucket shapes; scripts/update-packaging.mjs fills their
version and checksums from the uploaded release so nobody pastes sums
by hand. The formula writes its own #compdef zsh file because the
output of qqq completion zsh is an eval script, not an fpath file.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
packages/tui/README.md: running it, keys, screens, the : commands, the local
settings, the keys terminals and tmux may take (with the alternate_on
rebinding), tests and the code layout. TRY-IT.md replaces the spike section
with the qqq ui section. scripts/parity-matrix.py gets a TUI status table
and a TUI summary; feature-matrix.md regenerated (TUI: 118 pass, 25
partial). Server findings: recordSearch advertised with no provider, and
the untyped multiStatistics group (statisticList).
Also: -o -field in : commands and qqq ui routes, ASCII title separators,
short F-key labels at 80 columns.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Clean tree, free tags, npm ci, the orb's generate-drift, build and test
steps, lint, npm pack dry runs and the binaries; then it prints the
signed tag, npm publish and gh release commands. It never tags,
pushes or publishes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
scripts/build-binaries.mjs (npm run binaries) writes
dist/qqq-<os>-<arch>[.exe] for macOS arm64 and x64, Linux x64 and arm64
(glibc), Windows x64 and, with --musl, Linux musl, plus SHA256SUMS. It
refuses to build when the compiled-in operations index no longer
matches the OpenAPI document or the CLI version constant disagrees with
package.json, stamps the commit and target into qqq --version, and
smoke-runs the host binary. x64 targets use the baseline runtime so they
run on pre-AVX2 CPUs and under Rosetta or QEMU.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Checks the CLI's compiled-in version against its package.json, the
client's UI_API_VERSION against the document's info.version, and every
workspace dependency against the workspace version, so a coordinated
bump cannot leave one behind.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CLI and core go to 1.0.0-rc.1 and the client to 1.8.0-rc.2, with the
workspace dependencies pinned to them. Every published package now has
repository, homepage, bugs, engines (Node 20+), publishConfig, explicit
exports and a files list that leaves out compiled tests and source
maps. The repository LICENSE is copied in on prepack because npm only
packs a LICENSE from the package root. core had no publishConfig, so a
publish would have gone to the public registry; it now targets GitHub
Packages like the others. The CLI and client packages get the README
their files lists already named.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bun test in packages/tui (npm run test:tui at the root): the whole TUI
against a fake server in the test renderer: Esc from record 4 lands on row
4 and after stepping to record 7 lands on row 7; server paging, sort and a
:filter; a contingent form where State waits for Country and narrows;
the process wizard through an asynchronous job to its summary; F10 asks.
Plus unit tests for column layout, the line editor under a key burst,
command-line splitting and theme tokens.
packages/tui/scripts/smoke.sh drives the real TUI in tmux against the
sample server through fourteen screens and keeps the captures.
The kernel's multiStatistics reading accepts statisticList, the spelling
the sample server sends. Lint covers packages/tui.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings in the extensions, related-records and query-gaps streams. The parity
matrix is regenerated from the merged STATUS table; process downloads already
use the shared safeFileName/saveDownload helpers from the base.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
qqq login --browser signs in with the authorization code and S256 PKCE on a
loopback redirect (RFC 8252): a one-shot listener on 127.0.0.1 takes the
callback, the state is checked, and the code is exchanged at the provider with
the verifier (no nonce, as UI-API-CLIENTS.md section 7 asks). --port,
--redirect-host and --no-open cover exact registered URIs and headless hosts.
A denied request, a forged state or a refused exchange exits 3 and saves
nothing; the new token is checked against the server before it is kept.
qqq logout --provider-logout revokes the refresh token (RFC 7009) and opens
the provider end-session URL with the ID token, which refreshes now keep. A
refresh the provider refuses becomes UNAUTHENTICATED (exit 3, sign in again).
qqq-dev-idp gains the authorize endpoint (Approve/Deny page or auto-approve,
loopback redirects and S256 only), the authorization_code grant, ID tokens,
revocation and end-session. The device flow was run end to end for the first
time against a sample on OAUTH2 (8078) and worked unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflicts resolved by keeping both sides: table get keeps the record view
(sections, links, reveal, help) and gains --join and --variant; create and
update keep uploads and parents and send the variant; filters keep the
DATE_TIME to UTC conversion and accept joined names; export keeps safe
file names. My server findings are renumbered 18 and 19 after the merged
13 to 17. Joined DATE_TIME values are now shown in the local zone too.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflicts: core index exports (records, render/files and extensions all kept), CLI context
(imports from both sides; UnsafeFileNameError exits 2 and ExtensionNotInstalledError exits 5),
server-findings (the presence finding is now #18, after related records' #16 and #17). The parity
matrix regenerates unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings in the DATE_TIME work (local-zone display and input, --utc, process input exit codes).
The record view now formats DATE_TIME through displayDateTime (the viewer's zone, --utc for
the raw instant, server-zoned text kept), and so do the child lists of table get
--associations and table children; create/update keep the base's conversion through the
form session, including the --parent and --upload paths. process.ts keeps the base's value
formatting with the unified safeFileName/saveDownload. Exports, TRY-IT sections and parity
STATUS merged; this branch's findings renumbered to 16 and 17; matrix regenerated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflicts: core index exports (both kept), CLI context imports (both), main.ts registrations
(extensions and help), server-findings (the presence finding renumbered to #16 after #11-#15).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
forms/datetime converts typed wall-clock times (2026-10-31T14:30) to UTC in
the TZ zone with Intl offsets: a time in a spring-forward gap moves forward,
a repeated fall-back time means its first occurrence, and an unchanged
wall-clock time keeps the original instant with its precision. Instants are
shown as 2026-10-31 14:30:00 CDT; display values the server formats in a
declared zone are kept; --utc shows raw instants.
The form session converts DATE_TIME on set (table create, update, copy and
process values), DATE_TIME validation now requires a date and time, --where
and --group criteria on DATE_TIME fields are converted (a date alone is local
midnight), and table get/query and process screens format in the zone.
Tests cover Chicago, London, Sydney and Kolkata DST edges; verified live on
fieldLab (create, get, --utc, TZ=Asia/Tokyo, filters, update across the gap,
fieldLab.bulkEdit values).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflicts resolved: core index keeps every export; table get keeps --key (Go To) and gains
the record view, --associations, --links, --follow, --reveal, --help-content; table export
keeps the new formats, --search and stdout with the kernel's safe default name; README module
table and TRY-IT keep both; server findings keep 11-14 from the base and renumber this
branch's to 15 and 16; parity STATUS merged row by row and the matrix regenerated. The record
view's help helpers are renamed helpEntryText and tableViewHelp, since the base's help and
process screen modules export helpText and tableHelp.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
TRY-IT gains the related-record and record-view commands as verified against the sample.
Parity: REL-001..005 and 009, REC-028 pass live; REL-006..008, REC-002, REC-029..041 are
partial because the sample declares no such metadata (kernel unit tests cover them).
Findings: downloadRecordFieldFile 500s on a BLOB without adornments, and the sample lacks the
adornment, help and composite-association metadata needed to check those rows live.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Keeps both export sets in the kernel index, renumbers the process findings to
13 and 14 after the query-gaps findings, and regenerates the parity matrix from
the merged STATUS table.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The kernel gains a screen model (process/screen): each step's components in
declared order become neutral blocks both heads render - HELP_TEXT and HTML as
text, VIEW_FORM pairs on every step, RECORD_LIST paged through GET .../records,
the validation review with its preview and summary, PROCESS_SUMMARY_RESULTS
lines, DOWNLOAD_FORM references (GET /download), WIDGET through the widget
render model, and step help content.
The runner presents each screen, asks the provider on every input step
(validation review included), restarts at backStep with isStepBack=true when
the provider returns BACK, cancels on an abort signal with POST .../cancel,
and checks min/max input records before init. A step whose components are
only a VIEW_FORM no longer counts its formFields as inputs.
qqq process run prints intermediate screens to stderr and the last as its
output, asks validate-or-skip and Submit? on a terminal, saves downloads to
--out-dir, and turns Ctrl-C into a server cancel (exit 130). Verified live on
sleepInteractive, greetInteractive, clonePeople, simpleThrow, placeAddressPick
and person.bulkEdit's review screen; findings 11 and 12 recorded.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The client document is now the sample server's live document (core plus esb, UI API 1.8.0) merged
with the extension documents of every qbit checked out beside this repository: health (qqq), alerts,
assistant, collaboration, data-analysis, db-performance, edi, qa-capture, quick-search,
release-notes, secured-data and sql-workbench. 202 operations, 136 of them from extensions.
- scripts/merge-spec.mjs records each extension's path prefix and operationIds in
info.x-qqq-extensions, and accepts an extension the core input already carries when its
operations are identical (the live document's esb), instead of failing on the duplicate.
- scripts/merge-extensions.mjs (npm run spec:merge-extensions) finds the documents, merges them and
writes openapi/extension-sources.json with each document's version, branch and commit.
- scripts/gen-operations.mjs adds `extension` to OperationInfo (from x-qqq-extensions, else the
/qbit/{name} or established prefix) and exports DOCUMENT_EXTENSIONS.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The orb's node_install_dependencies runs npm run prepublishOnly (now the workspace build) and
node_run_tests runs npm test then npm run lint. lint checks license headers and that no
package imports another package's src. Also records finding 10: the sample app has no
uploaded-file archive table, so bulk load cannot be exercised against it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs/parity/feature-matrix.md keeps the 401 Next UI row ids and adds a CLI and a TUI column
(pass, todo, partial, n/a with reasons), computed by scripts/parity-matrix.py from the copied
Next matrix (docs/parity/next-feature-matrix.md) and the status table in the script. First fill
of the CLI column after the M1 work so far.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
qqq <noun> <verb> for table (list, describe, query, count, get, create, update, delete, export),
process (list, describe, run) and job (list, get, logs, cancel, retry), with the 0.1 flat
commands kept as hidden aliases. Global --output json|table|text|csv|yaml, --json, --query
(JMESPath), --fields, --yes and --trace work before or after the subcommand. Filters take
--where, --group (AND/OR with parentheses), --or, --order, --filter-json, --print-filter and
--count; queries page with --limit, --skip and --all. qqq api <operationId> calls any
operation from the generated index (--path, --param, --body-json, --form, --bytes, --out).
Exit codes: 2 usage and bad filters, 3 auth, 4 unreachable or rate limited, 5 not found,
6 validation, 70 server error. The realtime stream can carry a session cookie for tail.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The client gains getAuthenticationMetaData, createSession (reads the HttpOnly sessionUUID
cookie) and a sessionCookie option; UI_API_VERSION is 1.8.0. qqq login branches on the
server's authentication type and stores a server session for MOCK and FULLY_ANONYMOUS; the
device flow stays for OAUTH2 and AUTH_0. The session goes as a cookie because a bearer
session cannot continue asynchronous process steps on the 4.1.0-SNAPSHOT sample server.
scripts/dev/qqq.sh runs the CLI from source so every commit is testable without a build.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
So the person testing always knows which commit the shell is, and whether it carries
uncommitted changes. TRY-IT.md explains that there is no install step.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Throwaway OpenTUI (React) spike on Bun: 10k x 40 grid, every-field-type form, and a real
UI API process run with an async job against the qqq sample server. Records the go
decision, the measured latencies, binary sizes, and two server findings (bearer session
cannot continue an async process step under MOCK auth; url-encoded process values are
silently ignored). Adds scripts/dev launchers so the client can be tried after every step.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- @qrun-io/qqq-ui-api-client: typed client generated (openapi-typescript +
openapi-fetch) from the merged UI API document; device-flow sign-in, refresh,
UiApiError (BasicErrorResponseV1), SSE reader
- @qrun-io/qqq-cli: qqq login/whoami/apps/tables/query/get/run/jobs/tail,
only through /qqq/v1 with Authorization: Bearer
- @qrun-io/qqq-ui-api-conformance: black-box suite + qqq-dev-idp
- scripts/merge-spec.mjs, Prism mock, CI and GitHub Packages publish workflows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>