Merge develop into release/0.1 for the next 0.1.0 RC #87
Open
gitea-actions Bot
wants to merge 14 commits from
develop into release/0.1
pull from: develop
merge into: QRun-IO:release/0.1
QRun-IO:feat/process-tools
QRun-IO:feat/writes-follow-the-person
QRun-IO:fix/atomic-confirm-and-closed-surface
QRun-IO:fix/tool-service-owns-policy-and-identity
QRun-IO:fix/tool-call-service-enforces-its-checks
QRun-IO:feat/tool-registry
QRun-IO:docs/actions-and-mcp-design
QRun-IO:release/0.1
QRun-IO:rc/develop-into-release-0.1-everyone
QRun-IO:feat/store-reads-as-trusted-code
QRun-IO:rc/develop-into-release-0.1-weekly
QRun-IO:feat/weekly-budget
QRun-IO:rc/develop-into-release-0.1-save
QRun-IO:feat/save-and-schedule-analysis
QRun-IO:rc/develop-into-release-0.1-analyze
QRun-IO:feat/analyze-records
QRun-IO:fix/turn-watchdog-never-hangs-develop
QRun-IO:fix/turn-watchdog-never-hangs
QRun-IO:fix/live-thread-placeholders
QRun-IO:merge/develop-into-release-0.1
QRun-IO:automation/qbit-rc-pending
QRun-IO:fix/transcript-stored-before-message-end-develop
QRun-IO:fix/transcript-stored-before-message-end
QRun-IO:main
QRun-IO:archive/qrun-github-cutover-20261101/ca9212731dc2/develop
QRun-IO:feat/openai-compatible-provider
QRun-IO:dependabot/maven/develop/maven-minor-and-patch-634f43d868
QRun-IO:dependabot/github_actions/develop/github-actions-0fdda1077f
QRun-IO:feature/orb-0.8-track
QRun-IO:feature/orb-0.7.18
QRun-IO:feature/orb-0.7.17
QRun-IO:feature/orb-standardize-release-0.1
QRun-IO:feature/orb-main-standardization
QRun-IO:feature/shared-codeql
QRun-IO:feature/orb-0712-stable-gate
QRun-IO:feature/orb-rc-worker
QRun-IO:feature/orb-0.7.11
QRun-IO:feature/central-ci
QRun-IO:feature/conversation-management
QRun-IO:feature/starter-prompts-from-access
QRun-IO:feature/turns-not-jobs
QRun-IO:feature/assistant-attachments
QRun-IO:feature/assistant-optional-provider-health
QRun-IO:fix/assistant-health-tile-wording
QRun-IO:feature/fail-soft-renders
QRun-IO:fix/records-render-and-stream-context
QRun-IO:ci/release-rc-secrets-inherit
QRun-IO:feature/ui-api-1.6
QRun-IO:fix/shutdown-hook-weak-runtime
QRun-IO:fix/help-bundle-version
QRun-IO:feature/health-checks
QRun-IO:feature/jobs-tray
QRun-IO:feature/help-bundle
QRun-IO:feature/transcript-redaction
QRun-IO:feature/foreign-keys
QRun-IO:feature/schema-indexes
QRun-IO:fix/assistant-defaults-rollback
QRun-IO:feature/durable-turns
QRun-IO:fix/assistant-defaults-guards
QRun-IO:feature/tables-populated-defaults
QRun-IO:feature/provider-spi-package
QRun-IO:feature/email-api
QRun-IO:feature/orb-0.6.9
QRun-IO:feature/write-mode
QRun-IO:feature/x1-y1-channel-wall
QRun-IO:feature/email-qbit
QRun-IO:feature/spec-endpoints-core-invoker
QRun-IO:feature/provider-api-module
QRun-IO:feature/p7-scheduled-tasks
QRun-IO:feature/bulk-load-tools
QRun-IO:feature/p6-rag
QRun-IO:feature/presentation-demo-shots
QRun-IO:feature/answer-presentation
QRun-IO:fix/core-alerts-rc5
QRun-IO:feature/p5-routing-fallback
QRun-IO:feature/p4-budgets
QRun-IO:feature/p3-hipaa-routing
QRun-IO:feature/p2-bedrock-vertex
QRun-IO:feature/p1-provider-registry
QRun-IO:feature/spec-providers-write-mode-wall
QRun-IO:feature/demo-pending-card
QRun-IO:feature/release-rc-automation
QRun-IO:feature/demo-screens
QRun-IO:feature/profiles-skills-access
QRun-IO:feature/ui-api-tools
QRun-IO:feature/strict-tools-grammar-fix
QRun-IO:feature/app-home-settings
QRun-IO:feature/fix-rename-version-labels
QRun-IO:feature/note-burned-rc
QRun-IO:feature/assistant-record-screens
QRun-IO:feature/robustness-test-flake
QRun-IO:feature/ga-only-release-tags
QRun-IO:feature/snake-case-table-names
QRun-IO:chore/qrun-repo-standard
QRun-IO:feature/pom-revision-comment
QRun-IO:feature/phase-0-1-assistant
No Reviewers
Labels
Clear labels
architecture
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
java
question
release:rc
secuurity
ux
wontfix
Architecture review: coupling, cohesion, UI API
Something isn't working
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Pull requests that update GitHub Actions code
Good for newcomers
Extra attention is needed
This doesn't seem right
Pull requests that update java code
Further information is requested
Merging this develop PR cuts the next release candidate
Admin UX / information architecture
This will not be worked on
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: QRun-IO/qbit-assistant#87
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Automated release-candidate cut.
Ordinary chat turns no longer create core tracking jobs, so they stay out of the Jobs tray and tables and no longer trigger a per-answer "finished" notification. Crash safety comes from the assistant's own turn table (leased, heartbeated assistant_turn rows, seq-numbered event replay and the abandoned-turn sweep), which is unchanged. Removes AssistantRunTracker, JobTrackingRunTracker, the config accessors and the tracking id on RunningTurn, with their tests. The panel's Stop (POST /turns/{id}/cancel) is unchanged and still covered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>A confirmed bulk load still launches a real durable job through POST /qqq/v1/processes/{p}/jobs. Its tool result now carries jobHref (the job's /app link) next to jobId and a record ref to the job, and the confirm response's href points at the job, so the conversation links to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>assistant_conversation is open to every assistant user (assistant.hasAccess or assistant.admin) through AssistantAccessPermissionChecker; rows stay locked to their owner, and the Assistant app and other tables stay admin-only. A conversation customizer limits user edits to title, pinned and status, refuses deleting a conversation with an answer in progress and deletes the child rows after a table delete, through the same store code as DELETE /conversations/{id}. Adds Archive, Restore and Delete conversations as standard streamed-ETL record actions on the table, acting only on rows the user can see. UI API extension assistant 1.6.0: conversation list items have running, PATCH /conversations/{id} accepts {"read": true}, and GET /config has conversationsTable when the session may query that table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>The panel reloads the conversation at message.end. A tool-using turn must already have stored the real title, the question and answer parts and the tool-call rows then, not the transcript redactor's placeholders (changed:v1, [], {}). The redactor is always attached to a turn, so this is independent of Secured Data. Fails without the flush-before-message.end ordering (RC.24), passes on develop and RC.25. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>A new read tool: group a table by one or two fields (a date field optionally by a calendar unit) and compute a count, sum, average, minimum, maximum or distinct count per group, with the usual filter. It is one call of the data-analysis qbit's pivot operation (POST /qbit/data-analysis/tables/{table}/pivot) through the same gateway and with the signed-in user's session, so table read, the table's analyze permission, field access and record locks decide as on the Analyses screens. The assistant only narrows it: data access READ on the table, fields whose values it may see, the policy's row cap. Offered only where the data-analysis extension is registered; no production dependency on that qbit. Tests run against the released data-analysis qbit served by the real UI API. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>On a host with QInstance.enforcePermissionsInCoreActions on, a person with assistant.hasAccess and without assistant.admin was refused before any model call ("Not authorized to access the assistant admin data."). The assistant reads its settings (policy, providers, models, prices, budgets, data access) and writes that person's messages, turns, tool calls, pending actions and usage through tables whose permission rule is assistant.admin, with the person's session. On a host without core enforcement nothing checked that rule for the store, so it was never seen. AssistantStore, the only package allowed to call QQQ data actions, now marks every one of its calls as the assistant's own service code (withPermissionBypass with a reason), the way qbit-collaboration and qbit-backups do. Only the table permission check is skipped, for those calls: - the session stays the person's: writes are attributed to them, and the owner record locks keep every other person's rows out (core does not relax record locks for a bypass); - the tables' permission rules are unchanged: the tables stay closed to that person in the UI, the UI API and the assistant's tools; - who may call is decided before the store: every operation of the assistant API requires assistant access, and the save rules of budgets and overrides require assistant.admin themselves. A person's own budget. GET /config has "budgets" (UI API extension assistant 1.8.0): the budgets scoped to that user or to a permission that user holds, with used, limit, percent, state and when the count starts again. Budgets shared by several people are not listed. The same lines ride beside the person's question (<assistant_budget>), so "how much of my budget have I used?" has an answer. CoreEnforcementTest runs on the fixture instance with core enforcement on. Run without the store change, four of its five tests failed at the first store read; the fifth (own budget) was written afterwards. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>engine.ToolCallService trusted its caller for several checks. The chat's turn engine made them around the service; a caller with no chat turn (an MCP endpoint, later) would have skipped them. The rule that now holds for any caller: a tool call runs as the signed-in person with their current permissions and budget, a refusal happens before any data action, and the call is logged with redacted input. 1. execute refuses a tool the person is not offered now. It builds the offer from the request's policy and session (once per scope). Before, only the turn engine's allowed-tools set did, and navigate and search_help ran although the policy had switched them off. 2. A request without a policy is refused. UiApiToolbox, KnowledgeTool and BulkLoadTools read a missing policy as "every tool"; they refuse. 3. A request whose context was made for another session than its own is refused, for a call and for a confirmation. ConfirmedToolCall carries the confirming session. 4. New executeAndLog: runs the call and writes the assistant_tool_call row, redacting the input itself, also for a refused call. Redaction and the row moved out of TurnEngine (redactInput is gone; what a presentation logs is AssistantTool.inputForLog). The turn engine runs its calls in parallel under its own time limit and writes rows in call order, so it uses the two halves, execute and log(request, outcome); an ArchUnit rule keeps every other class on executeAndLog. The chat's rows are unchanged. 5. The service's javadoc says what it enforces and what a caller still supplies (rate limiting, per-request call limits and timeouts, classification). 6. The confirm route refuses while the kill switch is on (503 KILL_SWITCH) and when the policy no longer allows the tool (403), before the pending action is claimed, so it stays pending. 7. Tool families are registered explicitly: the QBit's own list (AssistantRuntime.ownToolFamilies) plus AssistantQBitConfig.withToolFamily for a host or another QBit. The classpath scan is gone. The registered names are logged once at startup; a duplicate name or an empty registry stops the start. 8. ToolRegistry.definitions sorts the keys of every object of an input schema, so two JVMs send the same bytes (a Map.of schema iterated in a per-JVM order, and the provider mappings serialise in iteration order). 9. The service itself requires assistant access, checks the kill switch (fresh) and the hard budget (the policy's hard limits and a stopped budget row, read without a budget event or alert, when a scope's first call arrives). New BudgetService.stoppedReason. Tests: ToolCallServiceTest (fixes 1 to 4, 6, 9, called with no turn), ToolRegistryTest (7, 8), ToolCallArchitectureTest (the new rule). ChatToolSetTest and ToolCallChecksTest pass unchanged. Test helpers that handed a tool a null policy now hand it the defaults. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>An independent security review of engine.ToolCallService (after PR 106) found what a caller could still hand it. The chat's turn engine is the only caller today and builds everything correctly, so none of it was reachable; it is closed before an MCP endpoint calls the service. The rule: every tool call runs as the signed-in person with their current permissions and budget, never as the assistant; a refusal happens before any data action; every call that ran has a log row with redacted input. Numbered as in the review: 1. A caller cannot supply a policy. The service reads it (PolicyService.current(false).tools(), narrowed for a scheduled task's conversation) and builds the tool Context itself; a caller hands it a tools.registry.ToolCall (person, id, name, input). Every change always shows a confirmation card (owner, 2026-11-12): ToolPolicy.writesRequireConfirmation is removed and UiApiToolbox always proposes. The assistant_policy column stays and is no longer read; no migration. 2. New SignedInPerson binds a session to the credentials forwarded to the UI API. No public constructor: ofRequest(Context), only for AssistantSpecs (ArchUnit), or ofSession(session). The context's gateway is always runtime.getUiApiGateway(). AssistantApi and TurnEngine lose withCredentials. 3. The hard budget is read on every call, not once per scope. executeAndLog(call, conversationId, hipaaRoute) makes the scope. 4. confirm(person, pendingActionId) replaces executeConfirmed, confirmationRefusal and logConfirmed: the service loads the stored action of that person, checks it, claims it once, runs it, marks it and writes the row. AssistantApi.confirmAction is a thin caller and the only one (ArchUnit). 5. The toolboxes cannot be called around the service: their entry points are package-private, the runtime has no getter for a toolbox (each family is handed its own), and ArchUnit forbids calls into tools.. from outside it except through the registry. The assistant's own reads (starter prompts, knowledge re-read) use the new read-only UiApiToolbox.read. 6. TurnEngine.executeTools writes each started call's row in a per-call finally: after an interruption of the engine, after a runtime exception while a result is processed or logged, and for a call cut short by its time limit, with the real outcome when the worker finished. A row that cannot be written for a call that changed data is logged at ERROR. 7. Whatever fails after a confirmation's claim ends in FAILED with a row, never in RUNNING. 8. The service checks that the scope's conversation and turn belong to the call's person and works out the row's client channel itself (ToolCallScope.clientChannel is gone). hipaaRoute stays the caller's; the javadoc says why. 9. AssistantQBitConfig.withToolFamily throws once the QBit is produced. Tests: ToolCallServiceTest (one per item, on the service directly), ToolCallRowsTest (item 6; all three fail on develop), and ToolCallArchitectureTest (items 2, 4, 5). ChatToolSetTest and ToolCallChecksTest pass with unchanged expectations. UiApiToolsSecurityTest's two tests that ran writes with confirmation off now propose and confirm. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>Package A2 of the actions design (docs/actions-and-mcp.md, sections 2 and 3), with two owner decisions of 2026-11-12, and five findings of an independent review of the tool boundary, each closed by the guarantee it gives. Writes follow the person - The data access of a table or process is declared in versioned code: AssistantQBitConfig.withDefaultTableAccess / withDefaultProcessAccess (both READ_WRITE) and a pin per object (withTableAccess, withProcessAccess). READ_WRITE grants nothing: the assistant may propose what the signed-in person may do themselves, and every change waits for their confirmation. - An administrator's assistant_data_access row can only narrow (READ, NONE). It never raises a code default, a pin or a table kept from AI. - Every assistant_* table and every process of the QBit is NONE by a pin in code that no row and no host pin lifts. - The write tools are offered to a person who may create, edit or delete records of some table (asked of the UI API as that person, once per offer, within the tool-call time limit; no answer means no write tool). GET /config features.writes is that answer for the person. - Settings that cannot be read, and were never read, allow nothing. - The table action "Add rows for every table" is retired. The changeset assistant-data-access-follow-user removes the unedited READ rows it wrote, and no other row. No answer without a spending limit - A person for whom no spending limit is in force (BudgetService.limitInForce) is refused: the turn before anything is stored, every model call, every tool call, every embedding request, a scheduled turn. Restoring a limit restores service on the next request. Review findings - A pending action is never marked expired over its outcome: expiry is a decision made through the same claim as confirming and cancelling. - No action stays RUNNING for ever: retention ends one left running for more than an hour as FAILED ("outcome unknown, check the record"), with a log row. - A person is the session the authentication module resolved, not the object passed in: a hand-made permission gives nothing, a revoked one is gone. - The assistant's own reads get their context from the tool-call service, for a person it checked; there is no public way to make a tool context from a session id, a gateway and a policy. - The published test-jar holds the testing package only. - AssistantStore's decision methods are final. See CHANGELOG.md and UPGRADE.md (what a host must apply before deploying). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.