Skip to content

Merge dev into main - #1329

Merged
WaylandYang merged 70 commits into
mainfrom
dev
Oct 20, 2026
Merged

WaylandYang merged 70 commits into
mainfrom
dev

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

Sync: everything landed on dev since the last sync (#1197), the last being #1325.

🤖 Generated with Claude Code

WaylandYang and others added 30 commits October 11, 2026 11:58
* Keep completed job work from repeating after a writeback failure

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>

* Link writeback recovery to its implementation review and validation

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>

* Remove duplicated writeback checks while preserving regression coverage

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>

* Keep writeback recovery in CI while trimming duplicated acceptance evidence

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>

---------

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
…clared ends (#1206)

Signed-off-by: Virtue039 <virtue039@qq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…essing job together (#1204)

Signed-off-by: Virtue039 <virtue039@qq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…back (#1211)

#1204 gave multipart uploads their first version row. The JSON push
(POST /kbs/{id}/ingest) creates its document through documents::create,
which still wrote none, so GET /documents/{id}/content answered 500 for
every pushed document. The document and version 1 are now written in one
transaction there too. Documents pushed before this are repaired by
migration 0111.

Signed-off-by: Wayland Yang <wayland0916@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
…sion (#1217)

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
… cases (#1202)

* Measure both namesake failure paths with fixed model replies in CI

Signed-off-by: Virtue039 <virtue039@qq.com>

* Give namesake fixtures distinguishing clues and require their named failures to remain observable

Signed-off-by: Virtue039 <virtue039@qq.com>

---------

Signed-off-by: Virtue039 <virtue039@qq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…f a question mark (#1220)

* A literal value reaches the adjudicator and the review card instead of a question mark

A fact whose object is a value (a title, an employee number, an amount)
has no entity on the other end, so its line read "phrase → ?". The
adjudicator and the review card both lost it; on the identity bench about
half of all live facts are values. The line now shows the value, cut to
120 characters. The two adjudicator rows of the scripted namesake cases
(#1202) that measured this leave the known-failure list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Wayland Yang <wayland0916@gmail.com>

* The review page test expects the literal value on its line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Wayland Yang <wayland0916@gmail.com>

---------

Signed-off-by: Wayland Yang <wayland0916@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#1221)

* Tests: Snowflake and Databricks error / lifecycle / edge paths via wiremock

Signed-off-by: Royce Lam <roycelam@umich.edu>

* Format the Snowflake and Databricks error-path tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Wayland Yang <wayland0916@gmail.com>

---------

Signed-off-by: Royce Lam <roycelam@umich.edu>
Signed-off-by: Wayland Yang <wayland0916@gmail.com>
Co-authored-by: Royce Lam <roycelam@umich.edu>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The middle tab lists relations only (kind = relation) but used the
Properties label, which reads 属性 in Chinese, the same as the Attributes
tab beside it: a user saw 属性 9 and 属性 2 and could not tell them apart.
It now uses the existing Relations label (关系).

Signed-off-by: Wayland Yang <wayland0916@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tons say relation (#1225)

The attributes tab of the ontology table had no button: the attribute
dialog took its class from the class panel that opened it, so the only
way in was to open a class first, and nothing on the tab said so. A user
looking at it asked whether attributes could be added by hand at all.
The tab now has New attribute; opened from there, the dialog asks which
class the attribute belongs to and will not save without one.

The relations tab, its button and the relation dialog used the
Properties strings, which read 属性 in Chinese, the word the attributes
tab uses. They now say 关系 / Relations (the edit title already did).

Signed-off-by: Wayland Yang <wayland0916@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ys at twelve (#1226)

Signed-off-by: Wayland Yang <wayland0916@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Zhoulu <ldazzh@lineadirecta.es>
Co-authored-by: Zhoulu <ldazzh@lineadirecta.es>
* A system admin can grant the flag to another user (#1233)

* Revoking your own system admin flag is refused (#1233)

* Granting a deactivated or unknown user is not found (#1233)

* PUT /admin/users/{id} grants or revokes system admin, admins only, audited (#1233)

* Member dialog: a system admin can switch another user's system admin flag (#1233)

* Route test: only a system admin grants the flag, and the grant is audited (#1233)

* cargo fmt
Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
)

* Specify cases that expect no property binding before changing their contract

Signed-off-by: Virtue039 <virtue039@qq.com>

* Expose negative case conflicts with human bindings in a concise contract

Signed-off-by: Virtue039 <virtue039@qq.com>

---------

Signed-off-by: Virtue039 <virtue039@qq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…1240)

Signed-off-by: dada-yan <BinjunYann@gmail.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…1241)

Signed-off-by: dada-yan <BinjunYann@gmail.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
)

* Add an export button to the ontology sidebar for the existing Turtle endpoint

Signed-off-by: Zhoulu <ldazzh@lineadirecta.es>

* Move the export button from Ontology to KbSettings and clarify that it exports the whole knowledge base

Signed-off-by: Zhoulu <ldazzh@lineadirecta.es>

---------

Signed-off-by: Zhoulu <ldazzh@lineadirecta.es>
Co-authored-by: Zhoulu <ldazzh@lineadirecta.es>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
* A write token may set up a base

Five routes take a personal token with write scope as well as a session,
acting as the token's owner: creating a base in a workspace, creating a
source, reading a source's push key, importing an ontology, and deciding a
kind word. Listing a workspace's bases takes any token. The person's role
is still the ceiling, a read token is refused with token_read_only, and a
token limited by kb_ids reaches only those bases and creates none
(token_kb_out_of_scope). One extractor, SessionOrToken, takes either
credential; AuthUser still reads every bearer as a JWT.

0014 gains decision 6.

Closes #1243

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* A token creates only statements sources, and audit rows name the token

A write token may create only a statements source; any other kind is
refused with token_source_kind, since url, rss and custom sources make the
server fetch on a schedule. source.created, alignment.kind_word_decided and
ontology.imported carry detail.token_id when a token acted, so an admin can
see what a token did before revoking it. Decision 6 of 0014 says so, and
that a system admin's write token acts as Owner in every base.

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

---------

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
* The ontology decides a pushed phrase it declares

After a pushed document's extraction, a signature whose phrase equals the
key or label of exactly one declared relation, with the subject's class in
its domain and the object's class in its range, binds without the model,
recorded as decided_by 'ontology'. Materialize is enqueued unless queued,
so the typed graph follows the push in a base with no chat model. A person's
decision is never overwritten, a near match or an ambiguous one goes to the
aligner, and the aligner leaves an ontology decision alone while the
relation still declares it.

Migration 0113 admits 'ontology' in phrase_bindings.decided_by; 0044 gains
a revision under decision 3.

Closes #1242

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* The ontology leaves state relations to the aligner

A state relation bound by the ontology carried no marks, and neither the
aligner nor the review queue would ever reach it. declared() now skips
temporal = 'state', so the aligner binds it and asks for marks. Tests: a
pushed phrase naming a state relation gets no ontology binding, and an
ontology binding is re-decided once its relation is narrowed or renamed.
0044's revision corrects the votes it records, says a prose statement
sharing a pushed signature is bound with it, and says how a binding whose
relation stops matching is re-decided.

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

---------

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…rd sign-in can be turned off (#1237)

* A verified email in an allowed domain is the one an SSO login may provision

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* An SSO login finds its account by verified email, or opens a viewer account with no password

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* SSO logins in an allowed domain link or open their account (UTOPIA_OIDC_ALLOWED_DOMAINS)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* UTOPIA_PASSWORD_LOGIN=false refuses password sign-in and registration

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* Login page shows only SSO when password sign-in is off

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* Document UTOPIA_OIDC_ALLOWED_DOMAINS and UTOPIA_PASSWORD_LOGIN

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* Address review on SSO provisioning (#1236)

- refuse provisioning when a deactivated account holds the email; open the
  account and link the subject in one transaction
- the server ignores UTOPIA_PASSWORD_LOGIN=false when OIDC is not configured,
  and warns at startup when no one unlinked could get in
- ADR 0069 records the trust assumption; 0066 points to it
- email_verified accepts true or "true" only
- unparseable password hashes still run argon2 against the dummy
- name claim trimmed, capped at 64, falls back to the local part
- exchange no longer returns the internal refusal reason
- password_login_disabled and an SSO-only unlinked message in en/zh

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

---------

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…'s chat draft, conversation or answers (#1238)

* The next account signed in on a tab does not see the previous account's chat draft, conversation or answers

Signed-off-by: dada-yan <BinjunYann@gmail.com>

* A tab clears its chat state when another account signs in, not on every sign-in

Signed-off-by: dada-yan <BinjunYann@gmail.com>

---------

Signed-off-by: dada-yan <BinjunYann@gmail.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…base no longer reads another base's quotes (#1261)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
…irst documents (#1256)

Signed-off-by: dada-yan <BinjunYann@gmail.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…ecision together (#1257)

* Adopting a proposal commits the element, its follow-up work and the decision together

Signed-off-by: dada-yan <BinjunYann@gmail.com>

* Name the pull request in the 0061 revision

Signed-off-by: dada-yan <BinjunYann@gmail.com>

---------

Signed-off-by: dada-yan <BinjunYann@gmail.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Signed-off-by: dada-yan <BinjunYann@gmail.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…ations (#1254)

Signed-off-by: dada-yan <BinjunYann@gmail.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Floating-Y and others added 27 commits October 18, 2026 11:41
…overlap (#1259)

* Keep an active system administrator when account removals overlap

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>

* Serialize administrator removals without blocking foreign key checks

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>

---------

Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
* The importer reads utopia:temporal on a property

The export has written utopia:temporal on every property that is not a
state since 0031, and the importer never read it back: an ontology that said
a relation is an event came in as a state, and the rule of #1242, which binds
a pushed phrase by the declaration only when the relation is not a state,
could never apply to an imported vocabulary.

The turtle projection reads urn:utopia:ns:temporal with one of state, event
or eternal; any other value is reported under the preview's unprojected
predicates and the property stays unset. A new relation is created with the
value, a re-import updates an existing relation's temporal to what the file
says, and a file that says nothing leaves it alone, so a value a person set
on the page survives an unannotated re-import. Attributes stay state.

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* The import reports a bad utopia:temporal under its IRI, the export writes state too, and an import that changes temporal moves updated_at

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

---------

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
* An end moved to the end of its period says so on the row

Signed-off-by: Maya Kid <BinjunYann@gmail.com>

* Name the pull request and the close-by-date issue in the 0053 revision

Signed-off-by: Maya Kid <BinjunYann@gmail.com>

* Leave the backfill to its own pull request and keep the mark's rule in one helper

Signed-off-by: Maya Kid <BinjunYann@gmail.com>

---------

Signed-off-by: Maya Kid <BinjunYann@gmail.com>
…s given, and the source cleanup reads only that base's statements. (#1307)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
…choose between candidates (#1309)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…ures only for keys not already settled (#1310)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
… seconds instead of three times a second (#1311)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
… reaches, so a service mirroring into Utopia can read its own base back without a person's session (#1263)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Signed-off-by: Floating-Y <118035379+Floating-Y@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
* Propose that a statement's role word follows its newest live evidence instead of the first value written, as of reads included

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* Narrow 0070 to supersession, back-fill role words onto every evidence chunk, give a role word its chunk's record time, and point 0037 at it

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* Rename 0070 to say that a role word speaks while its chunk is live, now that no ordering decides between live rows

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

---------

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
… base switcher, the chat scope chip, the token picker and My knowledge bases (#1250)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…ges (#1314)

Signed-off-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Signed-off-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
* A push that names its external_id keeps that id's document, so identical content under another id is reported as a duplicate instead of renaming the first, vanishing as unchanged, or failing the push

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

* Tombstone then push renames a declared id, and a duplicate answer leaves no restore or missing-flag change behind

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>

---------

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…n words such as line ranges and amounts are no longer stored but never shown (#1272)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…, as containment and type drift already do (#1308)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…aiting (#1304)

* The inference scheduler queues a base again only when it has no job waiting

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>

* The inference scheduling test runs in a binary of its own, since it queues every due base in the database

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>

---------

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…on it (#1305)

* Raising a model's concurrency limit admits the tasks already waiting on it

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>

* A changed model limit applies when it is saved, and raising one only adds permits

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>

---------

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…#1318)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
#1320)

Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Signed-off-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
Signed-off-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: Aschenbath <Aschenbath@users.noreply.github.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
… to the token ceiling (#1306)

* A reply that keeps repeating itself comes back cut instead of running to the token ceiling

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>

* Only a whole-row loop is cut, and the cut is reported as repetition rather than the token ceiling

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>

---------

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
…behind the extraction backlog (#1324)

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
… the files that changed since the last sync (#1325)

Signed-off-by: Quintin Botes <quintinbotes@outlook.com>
Co-authored-by: WaylandYang <145302500+WaylandYang@users.noreply.github.com>
@WaylandYang
WaylandYang merged commit 7ecf991 into main Oct 20, 2026
12 checks passed
@WaylandYang WaylandYang mentioned this pull request Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants