From 69d501a90e3fb4c7d4796496f9199f19c93e1a03 Mon Sep 17 00:00:00 2001 From: Wayland Yang Date: Sun, 27 Sep 2026 15:35:30 +0800 Subject: [PATCH] Cut every record's status line to one line, and keep one index by domain Status lines had become change logs: five ran past a thousand characters, the README repeated them in a second copy, and docs/design kept a third table that stopped at 0045. Each record now says its status in one line (a status word, the date and PR, what is open); the line that stood before is kept verbatim at the end of the record under Status history. The README has one index grouped by domain that repeats only the status word, so an implementing PR edits the record and not the README. No file is renamed or renumbered and no record's body changes. Co-Authored-By: Claude Fable 5.1 Signed-off-by: Wayland Yang --- .../0001-ontology-import-and-governance.md | 16 +- docs/decisions/0002-reasoning-engine.md | 13 +- docs/decisions/0003-ontology-growth-loop.md | 14 +- .../0004-language-and-localization.md | 14 +- docs/decisions/0005-alert-center.md | 11 +- .../0006-ontology-scale-and-the-prompt.md | 11 +- ...007-who-decides-what-becomes-a-relation.md | 16 +- .../0008-ontology-packs-as-cold-start.md | 12 +- docs/decisions/0009-no-type-is-a-type.md | 17 +- .../0010-no-relation-is-no-relation.md | 13 +- .../decisions/0011-a-mapping-is-not-a-fact.md | 13 +- ...ontology-is-a-contract-not-a-suggestion.md | 8 +- ...3-a-source-should-hand-over-its-history.md | 8 +- ...ty-from-the-person-scope-from-the-token.md | 8 +- ...ding-a-sentence-is-not-asserting-a-fact.md | 8 +- ...-the-open-seams-before-cutting-new-ones.md | 8 +- .../0017-a-contradiction-points-upstream.md | 8 +- ...0018-the-lakehouse-is-one-protocol-away.md | 8 +- .../0019-the-second-clock-can-be-rewound.md | 8 +- .../0020-an-auditor-reads-it-without-us.md | 8 +- ...e-reads-attributes-and-concludes-a-type.md | 8 +- ...0022-an-unknown-date-is-not-an-open-one.md | 8 +- ...0023-rss-observations-are-not-documents.md | 2 +- .../0024-the-world-axis-reaches-the-second.md | 8 +- ...ance-reads-the-ledger-before-it-decides.md | 8 +- docs/decisions/0026-a-decision-records-why.md | 8 +- ...atic-merge-is-gated-by-what-it-can-undo.md | 8 +- ...28-the-adjudicator-looks-before-it-asks.md | 8 +- docs/decisions/0029-a-rule-may-say-or-once.md | 8 +- ...0-a-rule-may-read-what-a-rule-concluded.md | 8 +- ...1-an-event-holds-at-the-moment-it-names.md | 17 +- .../0032-a-rule-computes-what-it-concludes.md | 8 +- ...-rss-source-summaries-are-source-scoped.md | 2 +- .../0034-an-action-is-a-declared-call.md | 2 +- .../0035-a-vector-index-is-built-by-a-job.md | 8 +- ...oration-aligns-a-schema-to-the-ontology.md | 17 +- ...7-a-relation-carries-its-own-attributes.md | 13 +- .../0038-the-interface-has-a-light-side.md | 12 +- .../0039-a-chunk-is-what-extraction-sees.md | 14 +- ...-a-chunk-says-where-its-words-came-from.md | 24 +- .../0041-a-name-is-a-claim-about-an-entity.md | 8 +- ...42-the-chat-loop-is-a-runner-with-hooks.md | 11 +- .../0043-every-review-queue-is-governed.md | 8 +- ...ology-is-a-view-over-what-documents-say.md | 8 +- ...ention-is-resolved-against-its-document.md | 8 +- docs/decisions/0046-the-app-surface-is-mcp.md | 3 +- .../0047-a-rule-may-conclude-a-relation.md | 8 +- ...ferences-stay-inside-the-knowledge-base.md | 2 +- ...ions-are-checked-when-a-rule-is-written.md | 2 +- ...eeps-its-identity-and-uncertain-outcome.md | 2 +- ...cision-carries-its-materialization-work.md | 8 +- ...052-document-content-is-a-read-contract.md | 2 +- ...cision-records-the-inputs-it-considered.md | 8 +- ...ay-push-statements-in-the-open-contract.md | 2 +- .../0060-a-rule-definition-has-a-history.md | 8 +- ...-open-graph-and-judged-by-its-questions.md | 8 +- .../0062-the-export-says-what-is-contested.md | 8 +- ...063-stopping-a-chat-ends-the-generation.md | 2 +- docs/decisions/README.md | 247 +++++++++--------- docs/design/README.md | 54 +--- 60 files changed, 475 insertions(+), 345 deletions(-) diff --git a/docs/decisions/0001-ontology-import-and-governance.md b/docs/decisions/0001-ontology-import-and-governance.md index 1d5f5dd4b..ef99219e0 100644 --- a/docs/decisions/0001-ontology-import-and-governance.md +++ b/docs/decisions/0001-ontology-import-and-governance.md @@ -1,14 +1,6 @@ # 0001 · Ontology import and governance -- **Status**: In progress. P0, P1, P2 and P2c built. P3's budget switch built - (`deployment_settings.ontology_prompt_budget`, default 24,000 characters; over budget the ontology - is retrieved per chunk, [0006](0006-ontology-scale-and-the-prompt.md)). P3a built but runs only by - hand. P3b built in a different shape: the surface predicate lands on - `fact_evidence.proposed_predicate`, and mapping back is `predicate_match` plus the adoption loop of - [0003](0003-ontology-growth-loop.md). P4a built (`entities.type_source`, #114); P4b and P4c pending. - P5 delivered by [0002](0002-reasoning-engine.md). The "argument order" half of criterion 2 - overturned by [0012](0012-the-ontology-is-a-contract-not-a-suggestion.md). Checked against the code - 2026-09-02. +- **Status**: In progress · P0–P2c, the P3 budget and P4a built; P5 delivered by 0002 · open: P3a runs by hand only, P4b, P4c - **Written**: 2026-08-27 / 28 · condensed into English 2026-09-03 - **Related**: [0002](0002-reasoning-engine.md) replaces P5's schedule; [0003](0003-ontology-growth-loop.md) is what P3b and P4 became; @@ -263,3 +255,9 @@ since moved to `scripts/bench/` (0012); these numbers are real but no longer com - `disjointWith` pruning of merge candidates on the resolution side is not done. - The `active` flag has only a governance use left (retired classes take no new entities); whether it is worth building waits for a real large ontology. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> In progress. P0, P1, P2 and P2c built. P3's budget switch built (`deployment_settings.ontology_prompt_budget`, default 24,000 characters; over budget the ontology is retrieved per chunk, [0006](0006-ontology-scale-and-the-prompt.md)). P3a built but runs only by hand. P3b built in a different shape: the surface predicate lands on `fact_evidence.proposed_predicate`, and mapping back is `predicate_match` plus the adoption loop of [0003](0003-ontology-growth-loop.md). P4a built (`entities.type_source`, #114); P4b and P4c pending. P5 delivered by [0002](0002-reasoning-engine.md). The "argument order" half of criterion 2 overturned by [0012](0012-the-ontology-is-a-contract-not-a-suggestion.md). Checked against the code 2026-09-02. diff --git a/docs/decisions/0002-reasoning-engine.md b/docs/decisions/0002-reasoning-engine.md index 811840f81..291058076 100644 --- a/docs/decisions/0002-reasoning-engine.md +++ b/docs/decisions/0002-reasoning-engine.md @@ -1,11 +1,6 @@ # 0002 · Reasoning engine -- **Status**: R0 built: six fact-level violation kinds in `axiom_violations` (five checks plus - `derived_contradiction`, 0017), eight ontology defect kinds in `ontology_defects`, two Review tabs. R1 built behind the KB switch `materialize_inferences` - (default on since 0050; off when this was written), rules from four axiom kinds (#132, #177, #179). R2 built as a proof chain - (`GET /kbs/{id}/derived/{id}/proof`, 0016 B1). Contradiction signals for derivations built per - [0017](0017-a-contradiction-points-upstream.md). R3 not built: every run recomputes the whole KB, - on `inference_interval_minutes` (default 60). +- **Status**: In progress · R0, R1 and R2 built · open: R3, incremental maintenance - **Written**: 2026-08-28 · condensed into English 2026-09-03 - **Related**: [0001](0001-ontology-import-and-governance.md) P5 (this record replaces its schedule); [0010](0010-no-relation-is-no-relation.md); @@ -98,3 +93,9 @@ corpus is cleaned, then materialization is switched on. - Materialize or evaluate at query time: 4.5× expansion on a small corpus, unknown at scale. For now a per-predicate cap and a periodic full recompute. - R3 incremental maintenance: correctness is the hardest to verify, and full recompute holds. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> R0 built: six fact-level violation kinds in `axiom_violations` (five checks plus `derived_contradiction`, 0017), eight ontology defect kinds in `ontology_defects`, two Review tabs. R1 built behind the KB switch `materialize_inferences` (default on since 0050; off when this was written), rules from four axiom kinds (#132, #177, #179). R2 built as a proof chain (`GET /kbs/{id}/derived/{id}/proof`, 0016 B1). Contradiction signals for derivations built per [0017](0017-a-contradiction-points-upstream.md). R3 not built: every run recomputes the whole KB, on `inference_interval_minutes` (default 60). diff --git a/docs/decisions/0003-ontology-growth-loop.md b/docs/decisions/0003-ontology-growth-loop.md index 0e727acec..41a6f88dc 100644 --- a/docs/decisions/0003-ontology-growth-loop.md +++ b/docs/decisions/0003-ontology-growth-loop.md @@ -1,12 +1,6 @@ # 0003 · The ontology grows out of the corpus -- **Status**: Built and running (#44; switch `knowledge_bases.auto_extend_ontology`, default on). Its - starting point has moved: `related_to` is deleted ([0010](0010-no-relation-is-no-relation.md)) and - the seed ontology retired (#125, #128), so a new KB starts from an ontology pack - ([0008](0008-ontology-packs-as-cold-start.md)) and this loop fills the gaps a pack leaves. - "Dismissal has memory" redone per [0007](0007-who-decides-what-becomes-a-relation.md). Two of three - known gaps closed (`adopt_proposed_types` + `entity_retypes`; `ontology_proposals`, #112); the "new - phrasings since you last looked" reminder still pending. Checked against the code 2026-09-02. +- **Status**: Implemented (#44) · open: the "new phrasings" reminder - **Written**: 2026-08-28 · condensed into English 2026-09-03 - **Related**: [0001](0001-ontology-import-and-governance.md) P3b and P4 are the plan, this is what grew; [0007](0007-who-decides-what-becomes-a-relation.md); [0008](0008-ontology-packs-as-cold-start.md); @@ -94,3 +88,9 @@ by this loop. The loop itself is unchanged and still on by default. - With the switch off there is no "88 new phrasings since you last looked"; the index `ontology_proposals_open_idx` was laid for it and the reminder never built. The only banner today is `last_auto_extension`, which reports the last automatic run. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Built and running (#44; switch `knowledge_bases.auto_extend_ontology`, default on). Its starting point has moved: `related_to` is deleted ([0010](0010-no-relation-is-no-relation.md)) and the seed ontology retired (#125, #128), so a new KB starts from an ontology pack ([0008](0008-ontology-packs-as-cold-start.md)) and this loop fills the gaps a pack leaves. "Dismissal has memory" redone per [0007](0007-who-decides-what-becomes-a-relation.md). Two of three known gaps closed (`adopt_proposed_types` + `entity_retypes`; `ontology_proposals`, #112); the "new phrasings since you last looked" reminder still pending. Checked against the code 2026-09-02. diff --git a/docs/decisions/0004-language-and-localization.md b/docs/decisions/0004-language-and-localization.md index aeb95f42f..23762b9d7 100644 --- a/docs/decisions/0004-language-and-localization.md +++ b/docs/decisions/0004-language-and-localization.md @@ -1,12 +1,6 @@ # 0004 · Language follows the reader of each text -- **Status**: Built · UI strings in `web/src/i18n/`; user-reachable server errors carry a - `code` (`AppError::Invalid`, 81 sites; 23 `Validation` guards stay English), and so do - the chat stream's `error` frames, in the same `{error, code}` body (2026-09-26); - `description` follows `knowledge_bases.ontology_lang`; LLM output for people takes - `locale` from the request; extracted data stays verbatim; the chat's step trail carries - fields the client words (`status`, counts, moments), and keeps its English `detail` for - stored messages (2026-09-26); the UI does not guess the browser language yet. +- **Status**: Implemented · open: the UI does not guess the browser language - **Written**: 2026-08-29 · condensed into English 2026-09-03 - **Related**: [0001](0001-ontology-import-and-governance.md) made `description` load-bearing; [0003](0003-ontology-growth-loop.md) separated `reason` from @@ -84,3 +78,9 @@ The last row is not copy. It is a quotation. and the Chinese did not". Edit both together. - **Mixed-language corpora in one knowledge base**: one language per KB; design it when it happens. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Built · UI strings in `web/src/i18n/`; user-reachable server errors carry a `code` (`AppError::Invalid`, 81 sites; 23 `Validation` guards stay English), and so do the chat stream's `error` frames, in the same `{error, code}` body (2026-09-26); `description` follows `knowledge_bases.ontology_lang`; LLM output for people takes `locale` from the request; extracted data stays verbatim; the chat's step trail carries fields the client words (`status`, counts, moments), and keeps its English `detail` for stored messages (2026-09-26); the UI does not guess the browser language yet. diff --git a/docs/decisions/0005-alert-center.md b/docs/decisions/0005-alert-center.md index 9fed687ce..941be30d4 100644 --- a/docs/decisions/0005-alert-center.md +++ b/docs/decisions/0005-alert-center.md @@ -1,9 +1,6 @@ # 0005 · The alert center -- **Status**: Built · five kinds live (`source.sync_failed`, `llm.unreachable`, - `llm.rate_limited` #160, `llm.out_of_credit` #182, `data_source.schema_sync_failed`); - the panel has search and per-group paging; decisions 1 and 2 were overturned during - implementation; `document.no_text_layer` still unwired. +- **Status**: Implemented · open: `document.no_text_layer` is unwired - **Written**: 2026-08-29 · condensed into English 2026-09-03 - **Related**: adjacent to the Review queue ([0001](0001-ontology-import-and-governance.md) P4); @@ -127,3 +124,9 @@ channel: something in this document did not land. None of it goes to Review. endpoint, so the message can say "no text layer; configure OCR and reprocess". When wired, do not ask when it is fixed: one row per scan, the panel folds adjacent rows, reprocessing needs no cleanup. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Built · five kinds live (`source.sync_failed`, `llm.unreachable`, `llm.rate_limited` #160, `llm.out_of_credit` #182, `data_source.schema_sync_failed`); the panel has search and per-group paging; decisions 1 and 2 were overturned during implementation; `document.no_text_layer` still unwired. diff --git a/docs/decisions/0006-ontology-scale-and-the-prompt.md b/docs/decisions/0006-ontology-scale-and-the-prompt.md index 20a6f86eb..ba4926a74 100644 --- a/docs/decisions/0006-ontology-scale-and-the-prompt.md +++ b/docs/decisions/0006-ontology-scale-and-the-prompt.md @@ -1,9 +1,6 @@ # 0006 · Ontology scale and the extraction prompt -- **Status**: Built · the character budget (`deployment_settings.ontology_prompt_budget`, - 24,000) and per-chunk retrieval are live, values unchanged; the "built-in classes always - present" floor is replaced by ancestor completion; the per-chunk list keeps to the same budget - and carries first sentences (#701); answer keys are still hand-filled. +- **Status**: Superseded by 0044 · was built - **Written**: 2026-08-29 · condensed into English 2026-09-03 - **Related**: [0008](0008-ontology-packs-as-cold-start.md) packs are now the starting ontology; [0012](0012-the-ontology-is-a-contract-not-a-suggestion.md) measured the bias @@ -90,3 +87,9 @@ classes), hence no hit rate. attributes) are guesses; tuning before the corpus question is settled only tightens the overfit. `run.mjs --packs schema-org,prov-o` walks the real cold-start path, but that comparison has not been run. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Built · the character budget (`deployment_settings.ontology_prompt_budget`, 24,000) and per-chunk retrieval are live, values unchanged; the "built-in classes always present" floor is replaced by ancestor completion; the per-chunk list keeps to the same budget and carries first sentences (#701); answer keys are still hand-filled. diff --git a/docs/decisions/0007-who-decides-what-becomes-a-relation.md b/docs/decisions/0007-who-decides-what-becomes-a-relation.md index 55d618be2..1618e9e3a 100644 --- a/docs/decisions/0007-who-decides-what-becomes-a-relation.md +++ b/docs/decisions/0007-who-decides-what-becomes-a-relation.md @@ -1,14 +1,6 @@ # 0007 · Counting decides what becomes a relation -- **Status**: Built · adoption by counting (`MIN_DOCS = 2`; an LLM run needs `MIN_SIGNALS - = 3`); six defects fixed; proposals persist in `ontology_proposals` (#112); open: - narrative verbs in the ontology, `merge_key` not folding `_by`; the starting point (10 - seeds, the `related_to` share) no longer exists · 2026-09-10: the counting path now - takes `temporal` from the proposal it already reads for classes and attributes, instead - of writing `state` for every relation it adopts — the comment that justified `state` - ("temporal has no consequence unless functional is set") stopped being true at - [0031](0031-an-event-holds-at-the-moment-it-names.md), which normalises every write by - it; counting still decides *whether*, the model only answers *which kind*. +- **Status**: Implemented (#112) · open: narrative verbs, `merge_key` not folding `_by` - **Written**: 2026-08-30 · condensed into English 2026-09-03 - **Related**: [0006](0006-ontology-scale-and-the-prompt.md) for the bench and its caveats; [0010](0010-no-relation-is-no-relation.md) and @@ -110,3 +102,9 @@ the corpus is dense in training data. `_by` into the group and mark it for swapping. - **What a 1,500-term start does to the adoption loop** has not been measured; no threshold has moved. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Built · adoption by counting (`MIN_DOCS = 2`; an LLM run needs `MIN_SIGNALS = 3`); six defects fixed; proposals persist in `ontology_proposals` (#112); open: narrative verbs in the ontology, `merge_key` not folding `_by`; the starting point (10 seeds, the `related_to` share) no longer exists · 2026-09-10: the counting path now takes `temporal` from the proposal it already reads for classes and attributes, instead of writing `state` for every relation it adopts — the comment that justified `state` ("temporal has no consequence unless functional is set") stopped being true at [0031](0031-an-event-holds-at-the-moment-it-names.md), which normalises every write by it; counting still decides *whether*, the model only answers *which kind*. diff --git a/docs/decisions/0008-ontology-packs-as-cold-start.md b/docs/decisions/0008-ontology-packs-as-cold-start.md index 9b56d3bc6..fa31e90c5 100644 --- a/docs/decisions/0008-ontology-packs-as-cold-start.md +++ b/docs/decisions/0008-ontology-packs-as-cold-start.md @@ -1,10 +1,6 @@ # 0008 · Ontology packs as the cold start -- **Status**: Built · five packs embedded in the binary (gzip, 1.7 MB → 316 KB), - multi-select at KB creation, 22-row static alignment table · a new KB seeds no - relations: the packs are the whole ontology · **no pack is checked by default and the - base made at registration installs none** (2026-09-10, #580) · the three open questions - stay open; the Chinese-label one got worse (2026-09-02 check) +- **Status**: Implemented · open: the record's three open questions - **Written**: 2026-08-30 · condensed into English 2026-09-03 - **Related**: [0001](0001-ontology-import-and-governance.md) criteria and IRI/key split; [0006](0006-ontology-scale-and-the-prompt.md) prompt budget; @@ -133,3 +129,9 @@ order status). - **Starting scale is a variable.** 0007's Snowball run showed that growing the vocabulary from 10 to 629 changes matching (49 recalls → 18). Nobody has studied what starting at 1500 does to the adoption loop; `MIN_DOCS = 2` and `MIN_SIGNALS = 3` were never retuned. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Built · five packs embedded in the binary (gzip, 1.7 MB → 316 KB), multi-select at KB creation, 22-row static alignment table · a new KB seeds no relations: the packs are the whole ontology · **no pack is checked by default and the base made at registration installs none** (2026-09-10, #580) · the three open questions stay open; the Chinese-label one got worse (2026-09-02 check) diff --git a/docs/decisions/0009-no-type-is-a-type.md b/docs/decisions/0009-no-type-is-a-type.md index b194bfb03..442130606 100644 --- a/docs/decisions/0009-no-type-is-a-type.md +++ b/docs/decisions/0009-no-type-is-a-type.md @@ -1,15 +1,6 @@ # 0009 · An undecided type stays empty -- **Status**: Implemented · `entities.type_id` and `entity_retypes.from_type_id` are - nullable; the nine builtin classes and the seeding function left with the seed relations - (#110 / #125 / #128 / [0011](0011-a-mapping-is-not-a-fact.md)) · `owl:disjointWith` now - reaches resolution (0016 B3): a declared disjointness, inherited down both hierarchies, keeps - same-name entities apart ahead of every heuristic; since #226 same-name entities of kin classes - (ancestor, descendant or a shared non-root ancestor) go to Review; `CONFUSABLE_TYPE_KEYS` stays - as the fallback when nothing is declared · `metric` / - `dimension` were created on demand by mapping exploration (#231); **they are to retire** - under [0036](0036-exploration-aligns-a-schema-to-the-ontology.md), which found them to be - the same species this record removed +- **Status**: Implemented · open: `metric` and `dimension` retire under 0036 - **Written**: 2026-08-30 · condensed into English 2026-09-03 - **Related**: [0008](0008-ontology-packs-as-cold-start.md) makes a real vocabulary the optional start; [0001](0001-ontology-import-and-governance.md) IRI/key split behind the @@ -114,3 +105,9 @@ in the ontology as a class, as if someone had decided it. - **Where `metric` / `dimension` belong.** Today they are builtin-on-demand. A "Utopia semantic layer" pack would move the last builtin classes out of code into something optional, with IRIs, replaceable (0016 D2). + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented · `entities.type_id` and `entity_retypes.from_type_id` are nullable; the nine builtin classes and the seeding function left with the seed relations (#110 / #125 / #128 / [0011](0011-a-mapping-is-not-a-fact.md)) · `owl:disjointWith` now reaches resolution (0016 B3): a declared disjointness, inherited down both hierarchies, keeps same-name entities apart ahead of every heuristic; since #226 same-name entities of kin classes (ancestor, descendant or a shared non-root ancestor) go to Review; `CONFUSABLE_TYPE_KEYS` stays as the fallback when nothing is declared · `metric` / `dimension` were created on demand by mapping exploration (#231); **they are to retire** under [0036](0036-exploration-aligns-a-schema-to-the-ontology.md), which found them to be the same species this record removed diff --git a/docs/decisions/0010-no-relation-is-no-relation.md b/docs/decisions/0010-no-relation-is-no-relation.md index 0b0386311..fd2b551d2 100644 --- a/docs/decisions/0010-no-relation-is-no-relation.md +++ b/docs/decisions/0010-no-relation-is-no-relation.md @@ -1,11 +1,6 @@ # 0010 · An unnamed relation stays empty -- **Status**: Implemented · `facts.predicate_id` is nullable, `related_to` is gone, display - falls back to the source's wording through `fact_surface_predicate(uuid)`, guarded by - `no_predicate_still_shows.rs` · both `mapped_to` follow-ups done with - [0011](0011-a-mapping-is-not-a-fact.md) (#126); the seed table and - `ensure_default_ontology` left with #128 · the two pieces of dead code noted 2026-09-02 are - cleared +- **Status**: Implemented - **Written**: 2026-08-30 (undated in the original; the day its migration ran) · condensed into English 2026-09-03 - **Related**: [0009](0009-no-type-is-a-type.md) the twin on the type side; @@ -97,3 +92,9 @@ fake vocabulary word. - 2026-09-02: two pieces of dead code. `graph.rs` `confirmed_mappings()` had zero callers (chat reads `mappings::confirmed`), and the `r.key = 'mapped_to'` join in `confirm_fact` matched zero rows. Both removed since (0016 A3). + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented · `facts.predicate_id` is nullable, `related_to` is gone, display falls back to the source's wording through `fact_surface_predicate(uuid)`, guarded by `no_predicate_still_shows.rs` · both `mapped_to` follow-ups done with [0011](0011-a-mapping-is-not-a-fact.md) (#126); the seed table and `ensure_default_ontology` left with #128 · the two pieces of dead code noted 2026-09-02 are cleared diff --git a/docs/decisions/0011-a-mapping-is-not-a-fact.md b/docs/decisions/0011-a-mapping-is-not-a-fact.md index 31745e608..ab1d03134 100644 --- a/docs/decisions/0011-a-mapping-is-not-a-fact.md +++ b/docs/decisions/0011-a-mapping-is-not-a-fact.md @@ -1,11 +1,6 @@ # 0011 · A mapping is configuration -- **Status**: Implemented · `concept_mappings` table and wiring (#126, the same commit as - this record), a standalone Data Mappings page (#140), moved out of the Review queue (#148) - · of the three things to rebuild, the Review flow and history are done, the evidence chain - is not · one of two open questions answered (2026-09-02 check) · **revised in part by - [0036](0036-exploration-aligns-a-schema-to-the-ontology.md)** (2026-09-09): what a mapping - is stands; what a concept is changes, and the table becomes a rendered one +- **Status**: Implemented (#126, #140, #148) · open: the evidence chain - **Written**: 2026-08-31 · condensed into English 2026-09-03 - **Related**: [0009](0009-no-type-is-a-type.md) removes the builtin entity classes, [0010](0010-no-relation-is-no-relation.md) the fallback relation, #125 the other eight @@ -103,3 +98,9 @@ rebuilding all three. - **One concept, several sources.** `(kb_id, concept_id, source)` allows a different definition per source, on purpose. Which one does querying use? Today all go into the prompt (cap 30) and the model picks; rules are easier to add now that it is a table. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented · `concept_mappings` table and wiring (#126, the same commit as this record), a standalone Data Mappings page (#140), moved out of the Review queue (#148) · of the three things to rebuild, the Review flow and history are done, the evidence chain is not · one of two open questions answered (2026-09-02 check) · **revised in part by [0036](0036-exploration-aligns-a-schema-to-the-ontology.md)** (2026-09-09): what a mapping is stands; what a concept is changes, and the table becomes a rendered one diff --git a/docs/decisions/0012-the-ontology-is-a-contract-not-a-suggestion.md b/docs/decisions/0012-the-ontology-is-a-contract-not-a-suggestion.md index e1cb60071..7c7761a65 100644 --- a/docs/decisions/0012-the-ontology-is-a-contract-not-a-suggestion.md +++ b/docs/decisions/0012-the-ontology-is-a-contract-not-a-suggestion.md @@ -1,6 +1,6 @@ # 0012 · The ontology is a contract -- **Status**: implemented · five controlled runs on `ai-timeline-ends` × schema.org + W3C Org took the violation rate from 57% to 4% and true reversals from 39 to 0 · the write-time judgment (`ontology::judge_direction`) now also covers adoption and merge (#190 / #196; merge reports `signature` rows into `axiom_violations`, R0 re-checks the same class) · filtering reified-shell relations out of pack import is still not done · since this record the ontology can also declare `inverseOf` / `subPropertyOf` (#177 / #179) and the pack list grew to five; the runs cover only the first two +- **Status**: Implemented (#190, #196) · open: the reified-shell filter at pack import - **Written**: 2026-08-31 (inferred: the source is undated and sits between 0011 and 0013, both dated 2026-08-31) · condensed into English 2026-09-03 - **Related**: [0008](0008-ontology-packs-as-cold-start.md) built the packs but never asked whether a large ontology holds up on real text; criterion 2 of [0001](0001-ontology-import-and-governance.md) ("the ontology guides, it does not enforce") is overturned by half here; [0010](0010-no-relation-is-no-relation.md) supplies the empty-predicate behavior; [0013](0013-a-source-should-hand-over-its-history.md) is the other end of the same line — how things come in, versus the rules they land by @@ -101,3 +101,9 @@ get picked by name or vector similarity. 0008 should carry this. nor retyped; the cause is unknown. - Pack import still lays out relations whose domain is a reified shell (`Action`, `Offer`, `LoanOrCredit`); shown to the model they can only produce violations. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · five controlled runs on `ai-timeline-ends` × schema.org + W3C Org took the violation rate from 57% to 4% and true reversals from 39 to 0 · the write-time judgment (`ontology::judge_direction`) now also covers adoption and merge (#190 / #196; merge reports `signature` rows into `axiom_violations`, R0 re-checks the same class) · filtering reified-shell relations out of pack import is still not done · since this record the ontology can also declare `inverseOf` / `subPropertyOf` (#177 / #179) and the pack list grew to five; the runs cover only the first two diff --git a/docs/decisions/0013-a-source-should-hand-over-its-history.md b/docs/decisions/0013-a-source-should-hand-over-its-history.md index e043e7db3..766040d0f 100644 --- a/docs/decisions/0013-a-source-should-hand-over-its-history.md +++ b/docs/decisions/0013-a-source-should-hand-over-its-history.md @@ -1,6 +1,6 @@ # 0013 · A source hands over its history -- **Status**: implemented for `github_issues` (#134), `jira_issues` (#135) and `notion` (#213, pages keep their own clock); WebDAV shares and object storage (S3 / Azure / GCS) arrived as plain file sources (#200, #207, #209) · Feishu and Confluence not started · GitHub and Jira write their timestamps to the second (#691, per 0024 §3) +- **Status**: Implemented for GitHub, Jira and Notion (#134, #135, #213) · open: Feishu, Confluence - **Written**: 2026-08-31 · condensed into English 2026-09-03 - **Related**: the bitemporal ground of [0001](0001-ontology-import-and-governance.md); the same judgment on the corpus side in `scripts/bench/fetch-wiki-history.mjs` (#122); [0012](0012-the-ontology-is-a-contract-not-a-suggestion.md) is the other end of the line — this record is about how things come in, that one about the rules they land by; the grant layer added afterwards (#142, `data_source_grants`) decides who may mount a source: provenance visible, destination governed @@ -126,3 +126,9 @@ shape — a class with an IRI drawn as a circle is a picture that lies. - 2026-09-03: every connector's credentials stay on the server (#246). Until now only `auth_header` was stripped from responses; the object-storage, WebDAV and Notion keys went out to every viewer. The keys now live in one list, `SOURCE_SECRET_KEYS`, shared by the listing, the create / update responses and the update merge (blank or missing keeps the stored value, an explicit `null` removes it). Adding a connector means adding its keys there first. - 2026-09-03: the five connectors added under this record could not be created (#247): the store's hand-written `KINDS` allowlist stopped at seven kinds while the sync dispatcher and the UI knew twelve. The kinds now come from one enum, `SourceKind` in `utopia-core`; the allowlist is derived from it, the dispatcher matches it exhaustively, and a test compares the frontend's `web/src/sourceKinds.ts` against it, so the three can no longer drift apart. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented for `github_issues` (#134), `jira_issues` (#135) and `notion` (#213, pages keep their own clock); WebDAV shares and object storage (S3 / Azure / GCS) arrived as plain file sources (#200, #207, #209) · Feishu and Confluence not started · GitHub and Jira write their timestamps to the second (#691, per 0024 §3) diff --git a/docs/decisions/0014-identity-from-the-person-scope-from-the-token.md b/docs/decisions/0014-identity-from-the-person-scope-from-the-token.md index 486655ddd..898b5fb63 100644 --- a/docs/decisions/0014-identity-from-the-person-scope-from-the-token.md +++ b/docs/decisions/0014-identity-from-the-person-scope-from-the-token.md @@ -1,6 +1,6 @@ # 0014 · Identity from the person, scope from the token -- **Status**: implemented (#161 record, #180 code) · `personal_tokens` and a Streamable HTTP MCP server at `POST /api/v1/kbs/{kb_id}/mcp` with five read-only tools, `application/json` responses rather than SSE · the account-level "Agents & tokens" page at `/account/tokens` (0016 A2): plaintext shown once beside a per-base client config snippet, the list keeps the prefix, revocation leaves a trace · `can_write` is still hard-coded `false`, so a `write` scope changes nothing yet +- **Status**: Implemented (#180) · open: `can_write` is hard-coded false - **Written**: 2026-09-01 · condensed into English 2026-09-03 - **Related**: migration `0014_data_source_grants` gave data sources a grant layer; this is the same question where a machine knocks. [0004](0004-language-and-localization.md) has the server speak English only, MCP error codes included. [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) removes the main objection to opening `remember` over MCP. @@ -80,3 +80,9 @@ push documents in. still unanswered. - **Tokens across workspaces.** `kb_ids` is a base-level whitelist; a workspace-level grant would look much like `data_source_grants`, and the two concepts may merge then. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented (#161 record, #180 code) · `personal_tokens` and a Streamable HTTP MCP server at `POST /api/v1/kbs/{kb_id}/mcp` with five read-only tools, `application/json` responses rather than SSE · the account-level "Agents & tokens" page at `/account/tokens` (0016 A2): plaintext shown once beside a per-base client config snippet, the list keeps the prefix, revocation leaves a trace · `can_write` is still hard-coded `false`, so a `write` scope changes nothing yet diff --git a/docs/decisions/0015-recording-a-sentence-is-not-asserting-a-fact.md b/docs/decisions/0015-recording-a-sentence-is-not-asserting-a-fact.md index 1d33c5840..c0b3e4b18 100644 --- a/docs/decisions/0015-recording-a-sentence-is-not-asserting-a-fact.md +++ b/docs/decisions/0015-recording-a-sentence-is-not-asserting-a-fact.md @@ -1,6 +1,6 @@ # 0015 · A recorded sentence waits for a nod -- **Status**: implemented · schema in migration `0018` (#180: `pending_facts`, `rejected_facts`) · runtime wired in [0016](0016-close-the-open-seams-before-cutting-new-ones.md) A1: extraction from a memory document goes to `pending_facts`, Review has a "waiting for your nod" queue placed first, a confirmation card grows into the chat after `remember`, `REMEMBER_ENABLED` is `true` again · decision 3 landed with different wording, see Revisions · MCP is still read-only; opening `remember` there is the next cut +- **Status**: Implemented (#180) · open: `remember` over MCP - **Written**: 2026-09-01 · condensed into English 2026-09-03 - **Related**: [0010](0010-no-relation-is-no-relation.md) removed the fallback relation (the empty predicate below is its correct behavior); [0011](0011-a-mapping-is-not-a-fact.md) rejected encoding a binary state as a float, the red line for this implementation; [0014](0014-identity-from-the-person-scope-from-the-token.md) kept MCP read-only mainly because of the confused deputy, which this gate removes @@ -88,3 +88,9 @@ original sentence above the extracted triples; triples alone ask for a judgment - Does the gate hold only memories, or every single-item interactive write? Today `remember` is the only such path; a future "add an edge by hand" interface should take the same table. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · schema in migration `0018` (#180: `pending_facts`, `rejected_facts`) · runtime wired in [0016](0016-close-the-open-seams-before-cutting-new-ones.md) A1: extraction from a memory document goes to `pending_facts`, Review has a "waiting for your nod" queue placed first, a confirmation card grows into the chat after `remember`, `REMEMBER_ENABLED` is `true` again · decision 3 landed with different wording, see Revisions · MCP is still read-only; opening `remember` there is the next cut diff --git a/docs/decisions/0016-close-the-open-seams-before-cutting-new-ones.md b/docs/decisions/0016-close-the-open-seams-before-cutting-new-ones.md index 089bb58a7..2e25b0944 100644 --- a/docs/decisions/0016-close-the-open-seams-before-cutting-new-ones.md +++ b/docs/decisions/0016-close-the-open-seams-before-cutting-new-ones.md @@ -1,6 +1,6 @@ # 0016 · Close the open seams before cutting new ones -- **Status**: in progress · the first schedule after v0.1.0 · A1, A2, A3 done · B1 done (#227) · B2 done (#238 / #243, [0017](0017-a-contradiction-points-upstream.md)) · B3 done: the signature half (#190 / #196), cross-pack signatures and range-aware direction (#233), `disjointWith` into resolution · D2's blank-base problem worked around with builtin `metric` / `dimension` classes (#231), the pack itself still planned · the lakehouse engines landed ahead of D4 (#239, [0018](0018-the-lakehouse-is-one-protocol-away.md)) · C1 done: a Wikidata answer key for the Wikipedia corpora (#289) · C2 done: type resolution queued after extraction, only the in-subtree tier applied on its own, on by default after that tier scored 39/41 against the Wikidata key (#297) +- **Status**: In progress · A, B1–B3, C1 and C2 done · open: B4, C3–C5, D - **Written**: 2026-09-02 · condensed into English 2026-09-03 - **Related**: written after checking [0001](0001-ontology-import-and-governance.md) through [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) against the code; every item below has its source in those fifteen records, whose 2026-09-02 revision notes are the check's product. This record only orders them and says why this order. @@ -100,3 +100,9 @@ implements or overturns and whether the status line moved. Written into the [REA connector boundary waits for Feishu. - After the benchmark: where the single-process worker and embedded Tantivy hit their ceiling decides whether v0.2 discusses horizontal scaling. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> in progress · the first schedule after v0.1.0 · A1, A2, A3 done · B1 done (#227) · B2 done (#238 / #243, [0017](0017-a-contradiction-points-upstream.md)) · B3 done: the signature half (#190 / #196), cross-pack signatures and range-aware direction (#233), `disjointWith` into resolution · D2's blank-base problem worked around with builtin `metric` / `dimension` classes (#231), the pack itself still planned · the lakehouse engines landed ahead of D4 (#239, [0018](0018-the-lakehouse-is-one-protocol-away.md)) · C1 done: a Wikidata answer key for the Wikipedia corpora (#289) · C2 done: type resolution queued after extraction, only the in-subtree tier applied on its own, on by default after that tier scored 39/41 against the Wikidata key (#297) diff --git a/docs/decisions/0017-a-contradiction-points-upstream.md b/docs/decisions/0017-a-contradiction-points-upstream.md index 808c2ecd9..93913abd0 100644 --- a/docs/decisions/0017-a-contradiction-points-upstream.md +++ b/docs/decisions/0017-a-contradiction-points-upstream.md @@ -1,6 +1,6 @@ # 0017 · A contradiction points at an error upstream -- **Status**: implemented · B2a (#238): engine and queue — `derive::contradictions`, migration 0020, the Review card with clues and repairs · B2b: contested edges in the alert colour and ghost edges for blocked derivations on the graph, the disputed chip on panel rows, the "did not land" section of the Derived tab with its proof chain · B2 of 0016, wider than the one line written there: contradictions become visible everywhere, not only as a new kind in the queue +- **Status**: Implemented (#238, #243) - **Written**: 2026-09-03 (conventions in [README](README.md)) - **Related**: the two unbuilt rows of the "derived vs asserted" table in [0002](0002-reasoning-engine.md) §2; [0016](0016-close-the-open-seams-before-cutting-new-ones.md) B2; the proof chain (B1, #227) supplies the "premises expand to the sentence" half of the card below @@ -203,3 +203,9 @@ B1 (#227) merges first after a rebase; B2a branches from it. selected. - **Does the disputed status need its own SSE event?** The `review` event is already sent; graph and panel can refetch on it. No new event. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · B2a (#238): engine and queue — `derive::contradictions`, migration 0020, the Review card with clues and repairs · B2b: contested edges in the alert colour and ghost edges for blocked derivations on the graph, the disputed chip on panel rows, the "did not land" section of the Derived tab with its proof chain · B2 of 0016, wider than the one line written there: contradictions become visible everywhere, not only as a new kind in the queue diff --git a/docs/decisions/0018-the-lakehouse-is-one-protocol-away.md b/docs/decisions/0018-the-lakehouse-is-one-protocol-away.md index 5ae15a5d8..59e240e7d 100644 --- a/docs/decisions/0018-the-lakehouse-is-one-protocol-away.md +++ b/docs/decisions/0018-the-lakehouse-is-one-protocol-away.md @@ -1,6 +1,6 @@ # 0018 · The lakehouse is one protocol away -- **Status**: implemented · `trino` / `databricks` / `snowflake` engines in `query_engine/` (migration `0021` widens the `engine` CHECK) · Trino has run against a real cluster and keeps a gated live test (#327); Databricks and Snowflake are covered by protocol replays only (wiremock) and **still want one** (#241, #242) · MaxCompute is not done, see the last section · the MySQL wire family this record skipped landed later in #303 (migration `0025`), so the order below is now "Postgres, MySQL, HTTP" as the `query_engine` header always had it +- **Status**: Implemented (#239) · open: Databricks and Snowflake against a real cluster (#241, #242), MaxCompute - **Written**: 2026-09-03 (conventions in the [README](README.md)) - **Related**: [0011](0011-a-mapping-is-not-a-fact.md) placed data sources at the deployment level and mounts at the base level; this record leaves that layer alone. [0016](0016-close-the-open-seams-before-cutting-new-ones.md) D4 put the MySQL wire protocol ahead of the lakehouse; the first section explains why the order flipped @@ -56,3 +56,9 @@ It is the one name of the four that is not "JSON in, JSON out": requests are sig - **How much schema to fetch.** All three expose `information_schema.columns`, and a lakehouse catalog can hold thousands of tables; `sync_schema_doc` caps at 200. With a schema in the connection string only that schema is read, otherwise the whole catalog. Whether that is enough waits for a real cluster. - **The type-restoration table** in `coerce` is hand-written from the three vendors' docs. Snowflake's `fixed` with a scale returns `"42.10"`, which becomes 42.1 and loses the trailing zero; harmless for a model, possibly not for an "exact definition". Revisit when the semantic layer keeps evidence (0016 D1) and decide whether to keep the raw string alongside. - **Trino's `ssl` inference**: a password, `ssl=true`, or port 443 / 8443 means https, anything else is plaintext. That is trino-python's rule, and someone who gets it wrong sees a TLS error instead of a hint. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · `trino` / `databricks` / `snowflake` engines in `query_engine/` (migration `0021` widens the `engine` CHECK) · Trino has run against a real cluster and keeps a gated live test (#327); Databricks and Snowflake are covered by protocol replays only (wiremock) and **still want one** (#241, #242) · MaxCompute is not done, see the last section · the MySQL wire family this record skipped landed later in #303 (migration `0025`), so the order below is now "Postgres, MySQL, HTTP" as the `query_engine` header always had it diff --git a/docs/decisions/0019-the-second-clock-can-be-rewound.md b/docs/decisions/0019-the-second-clock-can-be-rewound.md index 456d323ab..338dc17cd 100644 --- a/docs/decisions/0019-the-second-clock-can-be-rewound.md +++ b/docs/decisions/0019-the-second-clock-can-be-rewound.md @@ -1,6 +1,6 @@ # 0019 · The second clock can be rewound -- **Status**: implemented in three cuts · #317 gave every graph read the `held_at` predicate and `as_of` beside `at`; #337 gave entities their clock by unwinding `entity_merges`; this record's second open question landed with retrieval (superseded chunks keep their vectors, and vector recall and chunk fetch both take `as_of`) · the control on the graph page is still open, and full-text recall is still "now" only · #549 found the one derived read that had kept `invalidated_at IS NULL`: `derived_for_entity` now takes `as_of`, so the entity panel and `entity_facts` over MCP rewind derivations with the assertions +- **Status**: Implemented (#317, #337, #549) · open: the control on the graph page (#307), full-text recall as of a moment - **Written**: 2026-09-04 (conventions in the [README](README.md)) - **Related**: [0003](0003-ontology-growth-loop.md) put adoption's rewrites on the same append path as human correction, so the prior state is still on disk; [0002](0002-reasoning-engine.md) built the proof chain on the same rows. #268 (deleting a document) is what made the gap urgent, and is deliberately a separate change @@ -38,3 +38,9 @@ Read-only throughout. No new column, no migration. - **Retrieval has the same two clocks.** Settled, with one half left standing. `replace_chunks` no longer clears `embedding` on a superseded chunk: the storage that decision saved was the smaller half — the chunk's **text** was already kept — and the price was that history could not be searched at all. Vector recall and chunk fetch now take `as_of`, and a document deleted after T comes back with its chunks (#268 leaves the tombstone). **Full text stays "now"**: Tantivy holds one version of a base, so a timed search returns correct hits and misses the ones only history has. Giving the index versions is a separate piece of work, not a filter. - **A proof has one version.** `fact_derivations` is rewritten in place when a kept conclusion is reproved ([0030](0030-a-rule-may-read-what-a-rule-concluded.md), cut 2), and `proof` reads it as it stands. So a rewound panel lists the derivations the engine had drawn by T (#549), but expanding one shows today's premises, with the retracted ones marked. Keeping every proof would mean versioning `fact_derivations` the way `facts` is versioned; the row's `derived_at` already says when the conclusion first stood, and nobody has asked for the rest. - **What the control is.** A second slider doubles the surface for a question most people ask rarely; a mode switch on the existing slider is cheaper but risks reading as the same axis, which is the confusion the section above is written to prevent. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented in three cuts · #317 gave every graph read the `held_at` predicate and `as_of` beside `at`; #337 gave entities their clock by unwinding `entity_merges`; this record's second open question landed with retrieval (superseded chunks keep their vectors, and vector recall and chunk fetch both take `as_of`) · the control on the graph page is still open, and full-text recall is still "now" only · #549 found the one derived read that had kept `invalidated_at IS NULL`: `derived_for_entity` now takes `as_of`, so the entity panel and `entity_facts` over MCP rewind derivations with the assertions diff --git a/docs/decisions/0020-an-auditor-reads-it-without-us.md b/docs/decisions/0020-an-auditor-reads-it-without-us.md index 7bce5ebf2..625078c65 100644 --- a/docs/decisions/0020-an-auditor-reads-it-without-us.md +++ b/docs/decisions/0020-an-auditor-reads-it-without-us.md @@ -1,6 +1,6 @@ # 0020 · An auditor reads it without us -- **Status**: implemented · `GET /api/v1/kbs/{id}/export?format=turtle|jsonld` streams the base as RDF; `rdf.rs` holds the mapping, `oxrdfio` the serialisers · SPARQL is still not here, and this record says why it can wait +- **Status**: Implemented · revised 2026-09-25 (#902) · open: SPARQL (#308) - **Written**: 2026-09-05 (conventions in the [README](README.md)) - **Related**: [0001](0001-ontology-import-and-governance.md) kept the imported file verbatim and projected what today's consumers can use — this is the first consumer pointing the other way. [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) and [0019](0019-the-second-clock-can-be-rewound.md) are what makes the export worth reading: the intervals and the lineage are the content, not the triples @@ -91,3 +91,9 @@ One of the five built-in packs is PROV-O, so a base that has it loaded already k - **A SPARQL endpoint.** The escape hatch over an in-memory Oxigraph projection was the original plan and stays a later cut. Someone who asked for "the reasoning behind this decision" wants a file they can keep; a query endpoint is the second thing they ask for, not the first. - **Import of our own export.** The export is not a backup format, and reading it back would need entity resolution to be told "these IRIs are already resolved". Nothing stops it later; nothing depends on it now. - **A button.** The API is the deliverable; where the download lives in the interface is a separate cut. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · `GET /api/v1/kbs/{id}/export?format=turtle|jsonld` streams the base as RDF; `rdf.rs` holds the mapping, `oxrdfio` the serialisers · SPARQL is still not here, and this record says why it can wait diff --git a/docs/decisions/0021-a-rule-reads-attributes-and-concludes-a-type.md b/docs/decisions/0021-a-rule-reads-attributes-and-concludes-a-type.md index 3d0b8a8a0..31fad8f18 100644 --- a/docs/decisions/0021-a-rule-reads-attributes-and-concludes-a-type.md +++ b/docs/decisions/0021-a-rule-reads-attributes-and-concludes-a-type.md @@ -1,6 +1,6 @@ # 0021 · A rule reads attributes and concludes a type -- **Status**: implemented · all four decisions built and the five phases done (#359) · `derived_facts` widened, `attribute_rules` authored through the ontology page, the evaluator running in the materialisation job, conclusions explained in the entity panel with their premises, and two read-only MCP tools (`list_rules`, `rule_matches`) · writing a rule stays out of MCP, see the open question · a conclusion's own text cannot be edited yet (delete and rewrite), the rule card's entity count is not clickable, and a rule-classified entity carries no canvas marker +- **Status**: Implemented (#359) · open: editing a conclusion's text, a canvas marker, a clickable entity count - **Written**: 2026-09-05 (conventions in the [README](README.md)) - **Related**: [0002](0002-reasoning-engine.md) built the derivation runner and ruled out a user-defined rule language for ontology axioms; this record adds one narrow rule kind that lives beside the axioms, not inside them. [0009](0009-no-type-is-a-type.md) made `type_id` nullable and a type a considered claim; a rule concludes a *second* type without touching the asserted one. [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) draws the asserted / derived line this conclusion sits below. From #277. @@ -80,3 +80,9 @@ A well with a 2023 reading that fires the rule and a 2025 reading that does not rule mark" — is answered by `rule_matches`. Revisit when someone reports losing track of a marked entity on the canvas, which is also when it will be clear what the marker should say. - **Where authored rules live for export.** If a deployment's rules are part of its ontology, RDF export (#308) has to say how — a rule is not an OWL axiom. Deferred to whenever export lands. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · all four decisions built and the five phases done (#359) · `derived_facts` widened, `attribute_rules` authored through the ontology page, the evaluator running in the materialisation job, conclusions explained in the entity panel with their premises, and two read-only MCP tools (`list_rules`, `rule_matches`) · writing a rule stays out of MCP, see the open question · a conclusion's own text cannot be edited yet (delete and rewrite), the rule card's entity count is not clickable, and a rule-classified entity carries no canvas marker diff --git a/docs/decisions/0022-an-unknown-date-is-not-an-open-one.md b/docs/decisions/0022-an-unknown-date-is-not-an-open-one.md index cd849d7e8..6684fd72d 100644 --- a/docs/decisions/0022-an-unknown-date-is-not-an-open-one.md +++ b/docs/decisions/0022-an-unknown-date-is-not-an-open-one.md @@ -1,6 +1,6 @@ # 0022 · An unknown date is not an open one -- **Status**: implemented in two cuts · #394: `world_axis` beside `record_axis`, `facts.attested_at` set by every writer and backfilled, every server read and both client filters on the read interval, `holds_from` / `holds_to` on edges and entity facts, and an undated ending **closes** the dated open row it ends (`close_with_unknown_end`) · second cut: the evaluator intersects premise intervals as read (`read_span`), a derived bound that came from an anchor carries no precision (migration 0031 loosens the derived CHECK), and the violations list judges "still open" by the read end · third cut (#393): a second anchor, `attested_to`, so a bare open row closes too · fourth cut (2026-09-11): a **dated** ending closes the open row the same way, and every superseding row carries the edge's qualifiers · fifth cut (2026-09-14, #679): the temporal engine places a value in its timeline by the same anchors the reads use — a row with no stated start is ordered by its earliest dated evidence, and a predecessor it supersedes closes as ended-unknown anchored there; an end the engine drew is marked (`facts.end_derived`, migration 0057) and every change to a timeline recomputes those ends from the rows it has; a deadline stated relative to an event is stored as written, flagged `relative`, and closes the dated one before it the same way +- **Status**: Implemented in five cuts (#394, #393, #679) - **Written**: 2026-09-06 (conventions in the [README](README.md)) - **Related**: [0003](0003-ontology-growth-loop.md)'s graph migration gave the end of a fact three states and refused to store a document's date as an indeterminate instant; this record keeps that refusal and puts the date in a column that says what it is. [0019](0019-the-second-clock-can-be-rewound.md) put the record-axis predicate in one place (`record_axis`) and kept `at` and `as_of` apart; this record does the same for the world axis. [0017](0017-a-contradiction-points-upstream.md) gave derived rows their own precisions, and [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md)'s evaluator intersects premise intervals — both inherit the rule below. From #345 and #352, both found by the temporal benchmark (#306). @@ -130,3 +130,9 @@ The slider and a timed question stop returning a fact before its evidence or aft - **A dated ending did not close the open row either** (2026-09-11). ~~"李文博于 2024-04-30 辞去董事职务" arrived as a state observation with no start and a stated end; `insert_fact_inner` had a path for an *undated* ending (above) and none for a dated one, so the ledger held `2020-01-10 → open` beside `open → 2024-04-30` and read the first as still holding. The same for a company taken off a court's list of defaulters.~~ Settled: a state observation with no start and a stated end meets the open row of the same assertion whose start is not later than that end and closes it through `close_superseded` — the same superseding path, the end dated and truncated to its precision; a repeated dated ending reuses the closed row and only moves its anchor earlier. The same cut made every superseding writer (`close_superseded`, `close_with_unknown_end`, `correct_interval`, the refinement in `insert_fact_inner`) copy the edge's qualifiers along with its evidence — before it, closing a directorship dropped its title ([0037](0037-a-relation-carries-its-own-attributes.md)). - **Retrieval is untimed.** Vector and full-text recall take `as_of` (0019) but no `at`; a chunk about 2025 answers a question about 2023 and the model is left to notice. Out of scope; noted so the benchmark's chat probe is read with that in mind. - **Showing the anchor.** Whether the panel should say "attested 2024-02-20" beside a blank start, so a person sees why the slider hides the edge before then. Deferred until the first cut has been looked at. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented in two cuts · #394: `world_axis` beside `record_axis`, `facts.attested_at` set by every writer and backfilled, every server read and both client filters on the read interval, `holds_from` / `holds_to` on edges and entity facts, and an undated ending **closes** the dated open row it ends (`close_with_unknown_end`) · second cut: the evaluator intersects premise intervals as read (`read_span`), a derived bound that came from an anchor carries no precision (migration 0031 loosens the derived CHECK), and the violations list judges "still open" by the read end · third cut (#393): a second anchor, `attested_to`, so a bare open row closes too · fourth cut (2026-09-11): a **dated** ending closes the open row the same way, and every superseding row carries the edge's qualifiers · fifth cut (2026-09-14, #679): the temporal engine places a value in its timeline by the same anchors the reads use — a row with no stated start is ordered by its earliest dated evidence, and a predecessor it supersedes closes as ended-unknown anchored there; an end the engine drew is marked (`facts.end_derived`, migration 0057) and every change to a timeline recomputes those ends from the rows it has; a deadline stated relative to an event is stored as written, flagged `relative`, and closes the dated one before it the same way diff --git a/docs/decisions/0023-rss-observations-are-not-documents.md b/docs/decisions/0023-rss-observations-are-not-documents.md index befcafacf..490335c45 100644 --- a/docs/decisions/0023-rss-observations-are-not-documents.md +++ b/docs/decisions/0023-rss-observations-are-not-documents.md @@ -1,6 +1,6 @@ # 0023 · RSS observations are not documents -- **Status**: Implemented in #326 +- **Status**: Implemented (#326) - **Written**: 2026-09-05 - **Discussion**: [maintainer feedback on #326](https://github.com/deeplethe/utopia/pull/326#issuecomment-5547020427). This record follows implementation; it does not claim the issue/record-before-code sequence in `CONTRIBUTING.md` was followed. diff --git a/docs/decisions/0024-the-world-axis-reaches-the-second.md b/docs/decisions/0024-the-world-axis-reaches-the-second.md index 38124214b..8fc78228a 100644 --- a/docs/decisions/0024-the-world-axis-reaches-the-second.md +++ b/docs/decisions/0024-the-world-axis-reaches-the-second.md @@ -1,6 +1,6 @@ # 0024 · The world axis reaches the second -- **Status**: implemented in one cut · migration 0033 widens the ladder to the second and adds the truncation CHECK on `facts`, `pending_facts` and `derived_facts`; `WORLD_PRECISIONS` and `truncate_to` in the store, every writer truncating; `parse_time` reads zoned clock times and folds a zone-less one to the day; `time_text`, `fmtTime` and `rdf::world_time` write the reduced ISO forms; a derived bound takes the precision of the premise that set it +- **Status**: Implemented (migration 0033) - **Written**: 2026-09-06 (conventions in the [README](README.md)) - **Related**: [0003](0003-ontology-growth-loop.md)'s graph migration gave each end of a fact its own precision and stopped the ladder at the day; [0019](0019-the-second-clock-can-be-rewound.md) keeps the record axis at the clock's own resolution; [0022](0022-an-unknown-date-is-not-an-open-one.md) made an anchor an instant. #351 and #414 made every printed time carry its own precision — and showed that the world axis had nowhere finer than a day to carry. @@ -45,3 +45,9 @@ A fact from the memory log holds from `2026-06-01T14:32Z` instead of from the da - **A document's own time zone.** A `documents.time_zone` — set from a source's metadata, or by a person for a folder — would let "at three" become an instant. Deferred until a corpus needs it; the rule above is safe without it. - **Machine event logs as facts.** If log lines are ever extracted into facts with millisecond stamps, the second is where they land. Revisit only if a question needs the millisecond. - **The duration of a sub-day fact's precision on the canvas.** The slider's histogram buckets by year, month or day; a minute-level fact is drawn at its day. Fine for now, and a UI question, not a ledger one. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented in one cut · migration 0033 widens the ladder to the second and adds the truncation CHECK on `facts`, `pending_facts` and `derived_facts`; `WORLD_PRECISIONS` and `truncate_to` in the store, every writer truncating; `parse_time` reads zoned clock times and folds a zone-less one to the day; `time_text`, `fmtTime` and `rdf::world_time` write the reduced ISO forms; a derived bound takes the precision of the premise that set it diff --git a/docs/decisions/0025-governance-reads-the-ledger-before-it-decides.md b/docs/decisions/0025-governance-reads-the-ledger-before-it-decides.md index 556f37ba3..f13ffe0e5 100644 --- a/docs/decisions/0025-governance-reads-the-ledger-before-it-decides.md +++ b/docs/decisions/0025-governance-reads-the-ledger-before-it-decides.md @@ -1,6 +1,6 @@ # 0025 · Governance reads the ledger before it decides -- **Status**: decision 10 revised 2026-09-14 (a shape that raises doubt goes to the second look, and what that look finds is applied, #700) · decision 3 revised 2026-09-07 (three clusters a round, #458) · decision 10 added 2026-09-07 (identity rules, name shapes, type families, the labeled set and `govern.mjs`) · decision 4 revised 2026-09-06 (the agent's own confidence decides, history moves the bar or blocks) · cut 1 implemented (#434) · migration 0035 adds `knowledge_bases.governance` and `agent_decisions`; `governance` in the store holds the precedent families, the first-in-first-out queue with its clusters, the gate and the table; the `govern` job in the server reads the switch, calls the model with precedents and applies or proposes; `?queue=agent`, `ReviewCounts.agent` and `POST /kbs/{id}/review/agent/{decision_id}` on the API · cut 3 (UI, #437) implemented: the switch in base settings, the Agent queue with its rows and answers, the proposal chip on a duplicate card whose Merge / Keep answers the proposal, the Agent section on the Overview · cut 2 implemented: migration 0036 adds `question`, `trace` and `calls` to `agent_decisions`; `governor` in the extract crate holds the tools, the opening and the step reader; `investigate` in the server job runs the loop for pairs the batch could not settle (decision 8) · cut 4 implemented: migration 0037 adds `knowledge_bases.governance_since`; `fuse` in the server job turns the switch off after two reverts of the agent's merges since it was turned on, within seven days, raises `governance.tripped` and writes the ledger (decision 9) · [0026](0026-a-decision-records-why.md) gives a decision its stated reason, hands the batch adjudicator the same precedents and quotes the reason into them · [0028](0028-the-adjudicator-looks-before-it-asks.md) opens the loop of cut 2 to the adjudicator with governance off and gives it a `consequences` tool +- **Status**: Implemented · cuts 1–4 (#434, #437) · decisions revised 2026-09-06, 2026-09-07 and 2026-09-14 - **Written**: 2026-09-06 (conventions in the [README](README.md)) - **Related**: [0016](0016-close-the-open-seams-before-cutting-new-ones.md) C2 gave a base its first automation switch, `auto_type_resolution`, and this record copies its shape; #428 asked for bulk and automatic handling of same-name pairs and got the batch path (#429, #430) this builds on; [0020](0020-an-auditor-reads-it-without-us.md) made the ledger complete enough to be read back; [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) keeps a person's own sentences out of any machine's reach. @@ -115,3 +115,9 @@ Duplicates that the agent proposed on carry the reason code `proposed`; pairs it - **Cross-base precedent.** A workspace's other bases may hold the same names. Kept to one base until someone asks. - **A budget for the batch.** The loop has a daily budget (decision 8); the batch does not, and since #458 a round makes up to three batch calls at once. A job runs twenty clusters and re-enqueues itself while backlog remains, which is what the adjudicator always did. Whether a base needs a cap on that too is a question for a large corpus. - **Tools the loop does not have yet.** The graph around a side beyond its direct facts, the entity's disambiguator history, a full-text search of the corpus. Add one when a deferred question keeps asking for it. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> decision 10 revised 2026-09-14 (a shape that raises doubt goes to the second look, and what that look finds is applied, #700) · decision 3 revised 2026-09-07 (three clusters a round, #458) · decision 10 added 2026-09-07 (identity rules, name shapes, type families, the labeled set and `govern.mjs`) · decision 4 revised 2026-09-06 (the agent's own confidence decides, history moves the bar or blocks) · cut 1 implemented (#434) · migration 0035 adds `knowledge_bases.governance` and `agent_decisions`; `governance` in the store holds the precedent families, the first-in-first-out queue with its clusters, the gate and the table; the `govern` job in the server reads the switch, calls the model with precedents and applies or proposes; `?queue=agent`, `ReviewCounts.agent` and `POST /kbs/{id}/review/agent/{decision_id}` on the API · cut 3 (UI, #437) implemented: the switch in base settings, the Agent queue with its rows and answers, the proposal chip on a duplicate card whose Merge / Keep answers the proposal, the Agent section on the Overview · cut 2 implemented: migration 0036 adds `question`, `trace` and `calls` to `agent_decisions`; `governor` in the extract crate holds the tools, the opening and the step reader; `investigate` in the server job runs the loop for pairs the batch could not settle (decision 8) · cut 4 implemented: migration 0037 adds `knowledge_bases.governance_since`; `fuse` in the server job turns the switch off after two reverts of the agent's merges since it was turned on, within seven days, raises `governance.tripped` and writes the ledger (decision 9) · [0026](0026-a-decision-records-why.md) gives a decision its stated reason, hands the batch adjudicator the same precedents and quotes the reason into them · [0028](0028-the-adjudicator-looks-before-it-asks.md) opens the loop of cut 2 to the adjudicator with governance off and gives it a `consequences` tool diff --git a/docs/decisions/0026-a-decision-records-why.md b/docs/decisions/0026-a-decision-records-why.md index b28b58048..497da599e 100644 --- a/docs/decisions/0026-a-decision-records-why.md +++ b/docs/decisions/0026-a-decision-records-why.md @@ -1,6 +1,6 @@ # 0026 · A decision records why -- **Status**: Implemented · migration 0038 adds `resolution_reviews.rationale`; every human decide path (`decide_review`, the batch, an answer to the agent, a manual merge from the entity panel) takes an optional rationale, keeps it on the row and writes it as `why` on the ledger event; `Precedent.why` carries it into the prompt lines and the `ledger_search` tool; the batch adjudicator now reads the same precedents and keys its verdict cache on them; ~~one confident pair in ten goes to a person~~ removed 2026-09-14 (decision 5, revised); the model's own `why` is written beside machine decisions +- **Status**: Implemented (migration 0038) - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: [0025](0025-governance-reads-the-ledger-before-it-decides.md) made governance read the ledger; this record makes what it reads worth reading. #356 asked for it. The impact gate (#357) and the investigating adjudicator (#358) build on it; a rationale vocabulary that converges is a rule in the sense of [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md). @@ -58,3 +58,9 @@ The duplicate card has an input beside Keep / Merge; the batch toolbar has one i - **An adjudicator that investigates** (#358). Answered by [0028](0028-the-adjudicator-looks-before-it-asks.md): the batch escalates its unsettled pairs into the governor's loop, with governance off too. - **The agreement rate.** The sample is gone (decision 5, revised). The rows a person answers under the agent (0025) carry both verdicts; nothing computes the rate yet. - **History on a merge target.** The merge shows under the withdrawals it caused in the same second, which is honest chronology and hard to read. Folding consequences under their cause is presentation, and belongs with the rationale it now has. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented · migration 0038 adds `resolution_reviews.rationale`; every human decide path (`decide_review`, the batch, an answer to the agent, a manual merge from the entity panel) takes an optional rationale, keeps it on the row and writes it as `why` on the ledger event; `Precedent.why` carries it into the prompt lines and the `ledger_search` tool; the batch adjudicator now reads the same precedents and keys its verdict cache on them; ~~one confident pair in ten goes to a person~~ removed 2026-09-14 (decision 5, revised); the model's own `why` is written beside machine decisions diff --git a/docs/decisions/0027-an-automatic-merge-is-gated-by-what-it-can-undo.md b/docs/decisions/0027-an-automatic-merge-is-gated-by-what-it-can-undo.md index d19df1b18..9b7d634cf 100644 --- a/docs/decisions/0027-an-automatic-merge-is-gated-by-what-it-can-undo.md +++ b/docs/decisions/0027-an-automatic-merge-is-gated-by-what-it-can-undo.md @@ -1,6 +1,6 @@ # 0027 · An automatic merge is gated by what it can undo -- **Status**: Implemented · `execution_gate` in the store: `impact_of` reads what a merge would touch, `hold` is the pure judgement; the batch adjudicator and the governor both ask it before an automatic merge, and a held pair goes to a person as `escalate_impact| `; the governor records its look as a proposal whose reason begins `held for a person` · exports and a per-deployment opt-in stay open · the roadmap's execution gate for agents' calls is this module's next caller +- **Status**: Implemented · open: exports, a per-deployment opt-in - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: #357 asked for it. [0019](0019-the-second-clock-can-be-rewound.md) made a merge undoable inside the graph, which is what makes inside and outside different things. The 0025 gate (confidence, precedents, two hard rules) is unchanged; this one sits after it. The things a merge can reach are [0002](0002-reasoning-engine.md)'s derivations, [0012](0012-the-ontology-is-a-contract-not-a-suggestion.md)'s violations and the alerts of [0005](0005-alert-center.md), the answers of the chat, and the exports of [0020](0020-an-auditor-reads-it-without-us.md). @@ -58,3 +58,9 @@ A pair the adjudicator would have merged shows in Duplicates with the held reaso - **Alerts as such.** The alert center's kinds are about pipelines and switches, not the graph; the path from a merge to an alert runs through the consistency check, which is why the contradiction stands in for it. If a direct path appears, it joins `impact_of`. - **Answers that cited a side without resolving it.** `sources` carries chunks and facts; `resolved` is the honest entity-level signal today. - **How often it holds.** Run the bench with reasoning on and count `escalate_impact`; if derivations hold most of a base, the derived rule wants a finer question than "any". + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented · `execution_gate` in the store: `impact_of` reads what a merge would touch, `hold` is the pure judgement; the batch adjudicator and the governor both ask it before an automatic merge, and a held pair goes to a person as `escalate_impact| `; the governor records its look as a proposal whose reason begins `held for a person` · exports and a per-deployment opt-in stay open · the roadmap's execution gate for agents' calls is this module's next caller diff --git a/docs/decisions/0028-the-adjudicator-looks-before-it-asks.md b/docs/decisions/0028-the-adjudicator-looks-before-it-asks.md index 8cc30e7f6..9d42f3751 100644 --- a/docs/decisions/0028-the-adjudicator-looks-before-it-asks.md +++ b/docs/decisions/0028-the-adjudicator-looks-before-it-asks.md @@ -1,6 +1,6 @@ # 0028 · The adjudicator looks before it asks -- **Status**: Implemented · `consequences` joins the second look's tools (facts, quotes, ledger, namesakes): what a merge would touch, read from 0027's gate, and whether the two types share a family · with governance off, the batch adjudicator sends the pairs it cannot settle through the same loop under the same daily budget, and every look is a row in `agent_decisions` so the Agent queue and the budget see it · the number that will justify or retire it comes from 0026's sample, split by `via` +- **Status**: Implemented - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: #358 asked for it and said when to build it. [0025](0025-governance-reads-the-ledger-before-it-decides.md) cut 2 built the loop this record reuses; [0026](0026-a-decision-records-why.md) keeps the sample that measures it; [0027](0027-an-automatic-merge-is-gated-by-what-it-can-undo.md) draws the boundary the loop now sees before it decides. @@ -51,3 +51,9 @@ With governance off, an unsettled pair that the loop decided shows in Merges or - **The number.** 0026's sampled pairs carry the machine's verdict in the reason and the person's decision in the ledger, with `via` saying whether the batch or the loop produced the verdict. When enough of them exist, the agreement rate split by `via` says whether the loop earns its calls with governance off; nothing computes it yet. - **Tools the loop still lacks** (0025's list): the graph beyond a side's direct facts, a full-text search of the corpus, the disambiguator history. - **Whether a deferred question should skip the queue** when governance is off and land on the card only. Today it does both. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented · `consequences` joins the second look's tools (facts, quotes, ledger, namesakes): what a merge would touch, read from 0027's gate, and whether the two types share a family · with governance off, the batch adjudicator sends the pairs it cannot settle through the same loop under the same daily budget, and every look is a row in `agent_decisions` so the Agent queue and the budget see it · the number that will justify or retire it comes from 0026's sample, split by `via` diff --git a/docs/decisions/0029-a-rule-may-say-or-once.md b/docs/decisions/0029-a-rule-may-say-or-once.md index 684ef6d85..a574f2d5a 100644 --- a/docs/decisions/0029-a-rule-may-say-or-once.md +++ b/docs/decisions/0029-a-rule-may-say-or-once.md @@ -1,6 +1,6 @@ # 0029 · A rule may say "or", once -- **Status**: implemented · `group_seq` on `attribute_rule_conditions` (migration `0039`, existing conditions default to group 0 so every rule keeps its meaning), the evaluator runs group by group with the combination cap per group and one dedupe by interval across groups, `not_in` beside `in` · the API carries `group` on a condition and defaults it to 0, so a caller that sends a flat list still sends one conjunction · the rule editor writes blocks and the table reads the sentence back with its "or" in it +- **Status**: Implemented (migration 0039) - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md) built the rule and made its conditions a conjunction; this record widens that shape by exactly one level and says why not further. [0002](0002-reasoning-engine.md) ruled out a user-defined rule language, which is the boundary this record stays inside. From #476. @@ -45,3 +45,9 @@ The rule reads as one sentence in the table — `A and B or C` — because it is - **Chaining is a separate question** (#477): a rule still cannot read what another rule concluded, and this record does not change that. The two are independent — this one is the shape of one rule, that one is how rules feed each other. - **Set membership is still literal.** `in` and `not_in` compare strings exactly, so the same category in another language does not match. 0021 left this open and it stays open. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · `group_seq` on `attribute_rule_conditions` (migration `0039`, existing conditions default to group 0 so every rule keeps its meaning), the evaluator runs group by group with the combination cap per group and one dedupe by interval across groups, `not_in` beside `in` · the API carries `group` on a condition and defaults it to 0, so a caller that sends a flat list still sends one conjunction · the rule editor writes blocks and the table reads the sentence back with its "or" in it diff --git a/docs/decisions/0030-a-rule-may-read-what-a-rule-concluded.md b/docs/decisions/0030-a-rule-may-read-what-a-rule-concluded.md index 7feca84e2..32544aeae 100644 --- a/docs/decisions/0030-a-rule-may-read-what-a-rule-concluded.md +++ b/docs/decisions/0030-a-rule-may-read-what-a-rule-concluded.md @@ -1,6 +1,6 @@ # 0030 · A rule may read what a rule concluded -- **Status**: cuts 1 and 2 implemented · the record, and the runner: a fixed point inside one `materialize()` (rounds bounded by `MAX_DEPTH`, rules loaded in id order so a run is reproducible), a concluded value back in the fact pool, a concluded typing joining the subject scope **as a condition** so it carries its interval and its premise, `fact_derivations` widened by `premise_derived_id` (migration `0040`) with a `derivation_premises` view so all six readers see both kinds, `proof()` walking the tree, and a kept row re-proved when its premises changed · five database tests · the page showing which rules feed which is the next cut +- **Status**: In progress · cuts 1–2 built (migration 0040) · open: the page showing which rules feed which - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md) built the rule; [0029](0029-a-rule-may-say-or-once.md) settled the shape of **one** rule, and this record settles how **several** compose — the two are independent. [0002](0002-reasoning-engine.md) built the axiom fixed point this borrows from, and left R3 (incremental maintenance) open, which this record does not close. **[0013](0013-a-source-should-hand-over-its-history.md) is where the invariant this overturns was written down**, in the DDL comment on `fact_derivations`. [0024](0024-the-world-axis-reaches-the-second.md) decides which premise sets a derived bound's precision, and a chained bound obeys it one level up. From #477. @@ -62,3 +62,9 @@ Reading the *previous* run's `derived_facts` as input stays forbidden, for exact - ~~A kept row can keep a stale proof.~~ **Closed in cut 2.** `wanted` is keyed by subject, predicate, value and interval, with premises outside the key, so a conclusion reached this round by a different path than last round used to keep its old `fact_derivations` — already true with asserted premises, and easier to hit with chains, where the stale premise can be a row that was just invalidated. The run now reads the stored premises of every kept row in one query and rewrites the ones that differ; the count is `reproved` in the report. - **How deep is worth drawing.** Twelve rounds is the bound, not the expectation. If real bases produce chains longer than two or three, the panel needs a shape for that rather than an ever-deeper nest. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> cuts 1 and 2 implemented · the record, and the runner: a fixed point inside one `materialize()` (rounds bounded by `MAX_DEPTH`, rules loaded in id order so a run is reproducible), a concluded value back in the fact pool, a concluded typing joining the subject scope **as a condition** so it carries its interval and its premise, `fact_derivations` widened by `premise_derived_id` (migration `0040`) with a `derivation_premises` view so all six readers see both kinds, `proof()` walking the tree, and a kept row re-proved when its premises changed · five database tests · the page showing which rules feed which is the next cut diff --git a/docs/decisions/0031-an-event-holds-at-the-moment-it-names.md b/docs/decisions/0031-an-event-holds-at-the-moment-it-names.md index 2b4053eca..51e1b8170 100644 --- a/docs/decisions/0031-an-event-holds-at-the-moment-it-names.md +++ b/docs/decisions/0031-an-event-holds-at-the-moment-it-names.md @@ -1,15 +1,6 @@ # 0031 · An event holds at the moment it names -- **Status**: implemented (#486) · `Validity::under` normalises every write by the predicate's `temporal` — in `insert_fact_inner`, so extraction, the nod and a person's own fact all pass through it, and in `correct_interval` · `world_axis` reads an event as the bucket it names and an eternal fact as open at both ends, and the evaluator's `read_span` says the same · the prompt marks `[event]` and `[eternal]` relations and tells the model what to write, for a base that has any · the export carries `utopia:temporal` on a property that is not a state · no schema change, and a row written before this reads correctly · the panel still prints an event as `T ~ T` and the ontology page still does not say what the three values do — that is the UI cut · 2026-09-10: corpus-grown relations now take `temporal` from the - proposal instead of `state` for all (#593, on [0007](0007-who-decides-what-becomes-a-relation.md)) · - the five packs were measured for the same gap and there is none to fill: of 946 schema.org - object properties, 60 in PROV-O, 32 in ORG and 73 in IOF-core, **no object property is an - event** — standard vocabularies reify an event as a class (`PublicationEvent`, - `prov:Generation`) and their object properties are states pointing at it, so - `create_relation_types_bulk` writing `state` is right by construction, not by omission; - IOF's `…AtAllTimes` family is the one `eternal` candidate and is left as `state` until a - base needs it; a person changes any of them on the ontology page and the read side picks - it up at once, since `world_axis` looks the predicate's `temporal` up at read time +- **Status**: Implemented (#486, #593) · open: the UI cut - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: [0003](0003-ontology-growth-loop.md)'s graph migration gave a relation three temporal semantics and gave the engine one. [0022](0022-an-unknown-date-is-not-an-open-one.md) put the world-axis read in one place; this record adds two branches there and nowhere else. [0024](0024-the-world-axis-reaches-the-second.md)'s ladder is what "the bucket it names" is measured on. [0013](0013-a-source-should-hand-over-its-history.md) hands ticket events over at day precision; those were the first rows this rule was wrong for. From #486. @@ -72,3 +63,9 @@ An event row from before this record has a start and an open end. `facts_holds_t - **The wording the tools give the model.** `time_text` phrases an event's interval the way it phrases a state's. Whether "on 2024-03-15" reads better than "from 2024-03-15 to 2024-03-15" for a timed answer is a prompt question, to be looked at with the tool traces. - **An attribute taken at a moment.** Attributes are created as `state` and the UI does not offer otherwise (0021's readings are states that a later reading closes). A reading that is a measurement *at* a time rather than a value *from* a time is not expressible today; nothing has asked for it. - **The end convention for states.** Noted above. "Until 2024-07" ends at the start of July under the current read; the bucket reading this record gives events would end it at the start of August. Left alone here. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented (#486) · `Validity::under` normalises every write by the predicate's `temporal` — in `insert_fact_inner`, so extraction, the nod and a person's own fact all pass through it, and in `correct_interval` · `world_axis` reads an event as the bucket it names and an eternal fact as open at both ends, and the evaluator's `read_span` says the same · the prompt marks `[event]` and `[eternal]` relations and tells the model what to write, for a base that has any · the export carries `utopia:temporal` on a property that is not a state · no schema change, and a row written before this reads correctly · the panel still prints an event as `T ~ T` and the ontology page still does not say what the three values do — that is the UI cut · 2026-09-10: corpus-grown relations now take `temporal` from the proposal instead of `state` for all (#593, on [0007](0007-who-decides-what-becomes-a-relation.md)) · the five packs were measured for the same gap and there is none to fill: of 946 schema.org object properties, 60 in PROV-O, 32 in ORG and 73 in IOF-core, **no object property is an event** — standard vocabularies reify an event as a class (`PublicationEvent`, `prov:Generation`) and their object properties are states pointing at it, so `create_relation_types_bulk` writing `state` is right by construction, not by omission; IOF's `…AtAllTimes` family is the one `eternal` candidate and is left as `state` until a base needs it; a person changes any of them on the ontology page and the read side picks it up at once, since `world_axis` looks the predicate's `temporal` up at read time diff --git a/docs/decisions/0032-a-rule-computes-what-it-concludes.md b/docs/decisions/0032-a-rule-computes-what-it-concludes.md index c356d2835..4ee5b3fb3 100644 --- a/docs/decisions/0032-a-rule-computes-what-it-concludes.md +++ b/docs/decisions/0032-a-rule-computes-what-it-concludes.md @@ -1,6 +1,6 @@ # 0032 · A rule computes what it concludes -- **Status**: cuts 1 and 2 implemented · the record, and the capability: `Expr` in `utopia-reason` (`Attr | Const | Arith`, depth capped at `MAX_EXPR_DEPTH`), a third conclusion kind `computed` with the tree in `attribute_rules.conclude_expr` (migration `0041`), an operand that may be an expression on the comparison ops, the evaluator building the combination **before** testing conditions (a computed threshold reads the readings this round picked), every attribute the expression touched landing in the premises, and a malformed tree refused where it is written rather than skipped at materialisation · seven evaluator tests and three database tests · **the picker is the next cut**, so today an expression is reachable through the API and not through the page · reaching an attribute **across a relation** is decided in this record and not yet built +- **Status**: In progress · cuts 1–2 built (migration 0041) · open: the picker, an attribute reached across a relation - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md) built the rule and made its conclusion a **constant**; this record makes it computable. [0029](0029-a-rule-may-say-or-once.md) widened the shape of one rule by one level of `or`; [0030](0030-a-rule-may-read-what-a-rule-concluded.md) let rules feed each other — this is the third axis and independent of both. [0002](0002-reasoning-engine.md) ruled out a user rule language, and this record says exactly what that ban does and does not forbid. [0024](0024-the-world-axis-reaches-the-second.md) governs the precision a computed bound inherits. From #488. @@ -69,3 +69,9 @@ If it is ever wanted it needs its own record, answering what a completeness clai - **How deep before the picker loses.** Stated above as a concession, not settled. One operator is certainly a picker; nobody has yet said what they need beyond that. - **Does a computed conclusion feed the next rule?** It should — [0030](0030-a-rule-may-read-what-a-rule-concluded.md) puts a concluded value back in the fact pool and says nothing about how the value was arrived at. Worth a test rather than an assumption. - **Rounding and display.** A ratio of two readings is a long decimal. What the ledger stores and what the panel shows are not necessarily the same, and neither is decided here. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> cuts 1 and 2 implemented · the record, and the capability: `Expr` in `utopia-reason` (`Attr | Const | Arith`, depth capped at `MAX_EXPR_DEPTH`), a third conclusion kind `computed` with the tree in `attribute_rules.conclude_expr` (migration `0041`), an operand that may be an expression on the comparison ops, the evaluator building the combination **before** testing conditions (a computed threshold reads the readings this round picked), every attribute the expression touched landing in the premises, and a malformed tree refused where it is written rather than skipped at materialisation · seven evaluator tests and three database tests · **the picker is the next cut**, so today an expression is reachable through the API and not through the page · reaching an attribute **across a relation** is decided in this record and not yet built diff --git a/docs/decisions/0033-rss-source-summaries-are-source-scoped.md b/docs/decisions/0033-rss-source-summaries-are-source-scoped.md index 1884ae162..a67926537 100644 --- a/docs/decisions/0033-rss-source-summaries-are-source-scoped.md +++ b/docs/decisions/0033-rss-source-summaries-are-source-scoped.md @@ -1,6 +1,6 @@ # 0033 · RSS summaries are scoped to the source being listed -- **Status**: Implemented · the source- and generation-scoped lateral landed in #462; the nested `rss_full_content` contract landed in #417's second cut +- **Status**: Implemented (#462, #417) - **Written**: 2026-09-06 (conventions in the [README](README.md)) - **Related**: [0023](0023-rss-observations-are-not-documents.md) established RSS observations as a separate responsibility from documents; #417 changes the public `SourceView` contract while fixing the scope of its RSS summary. diff --git a/docs/decisions/0034-an-action-is-a-declared-call.md b/docs/decisions/0034-an-action-is-a-declared-call.md index 2b157b2a8..03c86f13d 100644 --- a/docs/decisions/0034-an-action-is-a-declared-call.md +++ b/docs/decisions/0034-an-action-is-a-declared-call.md @@ -1,6 +1,6 @@ # 0034 · An action is a declared call -- **Status**: cut 1 · this record. No code yet +- **Status**: Proposed · no code - **Written**: 2026-09-08 (conventions in the [README](README.md)) - **Related**: [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md) built the rule whose conclusion will one day fire an action; this record builds the thing it will fire and stops there. [0032](0032-a-rule-computes-what-it-concludes.md) drew the line this record keeps: everything a person authors is structured, so the page shows what runs. [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) is why an agent will not call one without a nod. [0020](0020-an-auditor-reads-it-without-us.md) is why every run is a row. The managed fetch path (#330, `http_fetch`) is the road a call travels. The grant layer follows what `data_source_grants` did for warehouses. diff --git a/docs/decisions/0035-a-vector-index-is-built-by-a-job.md b/docs/decisions/0035-a-vector-index-is-built-by-a-job.md index 5a5f55284..b43172412 100644 --- a/docs/decisions/0035-a-vector-index-is-built-by-a-job.md +++ b/docs/decisions/0035-a-vector-index-is-built-by-a-job.md @@ -1,6 +1,6 @@ # 0035 · A vector index is built by a job -- **Status**: implemented · a partial HNSW index per dimension on `chunks.embedding` and `entities.profile_embedding`, requested by the first write of that dimension and built by a `build_vector_index` job outside any transaction · `vector_search` and `nearest_typed_entities` write the dimension as a literal, cast both sides and set `hnsw.iterative_scan = relaxed_order` · type resolution gathers a batch's neighbours eight at a time, in order, and remembers descendant sets per batch (#512, #514) · dimensions above 2000 stay on the exact path +- **Status**: Implemented (#512, #514) - **Written**: 2026-09-09 (conventions in the [README](README.md)) - **Related**: the ingest migration said P1 scans sequentially and an HNSW index comes "at volume"; this is that note coming due. [0019](0019-the-second-clock-can-be-rewound.md) is why the record-axis filter stays on the query and the index accommodates it. [0016](0016-close-the-open-seams-before-cutting-new-ones.md) C2 is the loop that scanned the entity table once per subject. @@ -72,3 +72,9 @@ Every question is asked twice, without the index and with it, and the answers mu - **Recall on a corpus the planner sends to the index.** The entity figure above (0.997) is on real profiles with the index forced; the chunk figures are on real chunks in an adversarial table, a synthetic tenant whose vectors sit closer to every query than the query's own base does, and they reach 1.0 only with the scan memory raised. A real deployment with two large tenants of different subject matter is the case still unmeasured, and `hnsw.ef_search` (40 here; 200 cost 10 ms in the synthetic run and changed nothing on the copy) is the first knob if it disappoints. - **A dimension that leaves.** When a workspace changes model, the old dimension's index stays until someone drops it. It is small harm and no mechanism yet. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented · a partial HNSW index per dimension on `chunks.embedding` and `entities.profile_embedding`, requested by the first write of that dimension and built by a `build_vector_index` job outside any transaction · `vector_search` and `nearest_typed_entities` write the dimension as a literal, cast both sides and set `hnsw.iterative_scan = relaxed_order` · type resolution gathers a batch's neighbours eight at a time, in order, and remembers descendant sets per batch (#512, #514) · dimensions above 2000 stay on the exact path diff --git a/docs/decisions/0036-exploration-aligns-a-schema-to-the-ontology.md b/docs/decisions/0036-exploration-aligns-a-schema-to-the-ontology.md index d4125935d..07df2f23f 100644 --- a/docs/decisions/0036-exploration-aligns-a-schema-to-the-ontology.md +++ b/docs/decisions/0036-exploration-aligns-a-schema-to-the-ontology.md @@ -1,15 +1,6 @@ # 0036 · Exploration aligns a schema to the ontology -- **Status**: written · decision 7 implemented (#553 → #561: the schema document is - indexed and never extracted, `sources.config.extract`, `graph_status = skipped`; the - column-name entities went from 93 to 12 on the wide bench base) · a definition can be - written by hand (#562 → #563), the door the seeded upper bound simulated · the - conventions-as-prose dead end was measured at 14/18 and revised in place (see Dead ends) · - cuts remaining: #554 alignment, #555 the conversion tree, #556 retiring the two classes · - overturns where a mapping hangs, and keeps what [0011](0011-a-mapping-is-not-a-fact.md) - said about what a mapping is · the two builtin classes `metric` / `dimension` are to - retire (their revision notes are on 0009 and 0011) · the migration that carries the - exploration ledger (#503) is unaffected +- **Status**: In progress · decision 7 built (#561, #563) · open: #554, #555, #556 - **Written**: 2026-09-09 (conventions in the [README](README.md)) - **Related**: [0011](0011-a-mapping-is-not-a-fact.md) moved a mapping out of the ledger and is right that it is configuration; this record moves the *concept* out of the entity table. @@ -231,3 +222,9 @@ changes one thing 0011 did not decide but its implementation assumed — that th mapping hangs from is an entity of a class called Metric. The concept is an attribute of a real class, or a rule over such attributes, and the mapping is how a column becomes that attribute's value. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> written · decision 7 implemented (#553 → #561: the schema document is indexed and never extracted, `sources.config.extract`, `graph_status = skipped`; the column-name entities went from 93 to 12 on the wide bench base) · a definition can be written by hand (#562 → #563), the door the seeded upper bound simulated · the conventions-as-prose dead end was measured at 14/18 and revised in place (see Dead ends) · cuts remaining: #554 alignment, #555 the conversion tree, #556 retiring the two classes · overturns where a mapping hangs, and keeps what [0011](0011-a-mapping-is-not-a-fact.md) said about what a mapping is · the two builtin classes `metric` / `dimension` are to retire (their revision notes are on 0009 and 0011) · the migration that carries the exploration ledger (#503) is unaffected diff --git a/docs/decisions/0037-a-relation-carries-its-own-attributes.md b/docs/decisions/0037-a-relation-carries-its-own-attributes.md index b69fdf2dd..528176c21 100644 --- a/docs/decisions/0037-a-relation-carries-its-own-attributes.md +++ b/docs/decisions/0037-a-relation-carries-its-own-attributes.md @@ -1,11 +1,6 @@ # 0037 · A relation carries its own attributes -- **Status**: cut 1 merged (#598) · cut 1b (units, auto-declaration, sibling currency; #600) · `relation_type_qualifiers` and `fact_qualifiers` - (migration 0049), a relation declares its qualifiers, extraction writes them, the panel and - the export read them · not in this cut: an entity-valued qualifier (the column is reserved, - nothing writes it), a second row plus a conflict when two mentions of one edge disagree - (today the first value stays and the disagreement goes to the drop report), the canvas - label, the bootstrap proposing qualifiers for a relation it adopts +- **Status**: In progress · cuts 1 and 1b built (#598, #600) · open: entity-valued qualifiers, disagreeing mentions, the canvas label - **Written**: 2026-09-10 (conventions in the [README](README.md)) - **Related**: [0031](0031-an-event-holds-at-the-moment-it-names.md) is what makes the same edge repeatable — an event's key includes its moment — and this record leans on it for @@ -148,3 +143,9 @@ rules corpus declared and undeclared, a Chinese corpus declared and undeclared): relations are states pointing at reified event nodes, so the natural home of an amount in schema.org is the event class, not the edge. No pack relation declares a qualifier by default. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> cut 1 merged (#598) · cut 1b (units, auto-declaration, sibling currency; #600) · `relation_type_qualifiers` and `fact_qualifiers` (migration 0049), a relation declares its qualifiers, extraction writes them, the panel and the export read them · not in this cut: an entity-valued qualifier (the column is reserved, nothing writes it), a second row plus a conflict when two mentions of one edge disagree (today the first value stays and the disagreement goes to the drop report), the canvas label, the bootstrap proposing qualifiers for a relation it adopts diff --git a/docs/decisions/0038-the-interface-has-a-light-side.md b/docs/decisions/0038-the-interface-has-a-light-side.md index a5b4cc829..762ba299c 100644 --- a/docs/decisions/0038-the-interface-has-a-light-side.md +++ b/docs/decisions/0038-the-interface-has-a-light-side.md @@ -1,10 +1,6 @@ # 0038 · The interface has a light side -- **Status**: Implemented (#599) · a `data-theme` on ``, a second token block in - `styles.css`, the canvas reads its colours from the tokens and re-reads them on a switch, - a `raw-colour` rule in the style guard · not in this cut: an entity palette tuned for - paper (the data colours are the same in both themes and must stay equal to the server's - copy), a theme choice that follows the account across browsers +- **Status**: Implemented (#599) · open: a palette for paper, a theme that follows the account - **Written**: 2026-09-11 (conventions in the [README](README.md)) - **Related**: [0004](0004-language-and-localization.md) put the reader's language in the reader's hands; the theme is the same kind of choice and lives in the same place, the @@ -131,3 +127,9 @@ rule for interface work is "look first" and not "green first". - **`styles.css` under the guard.** The token blocks are the only place a value may appear in the stylesheet; nothing enforces it. A rule that scans the stylesheet below its token blocks is a small addition when it is next touched. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented (#599) · a `data-theme` on ``, a second token block in `styles.css`, the canvas reads its colours from the tokens and re-reads them on a switch, a `raw-colour` rule in the style guard · not in this cut: an entity palette tuned for paper (the data colours are the same in both themes and must stay equal to the server's copy), a theme choice that follows the account across browsers diff --git a/docs/decisions/0039-a-chunk-is-what-extraction-sees.md b/docs/decisions/0039-a-chunk-is-what-extraction-sees.md index 16fb6973f..f47cfe7e6 100644 --- a/docs/decisions/0039-a-chunk-is-what-extraction-sees.md +++ b/docs/decisions/0039-a-chunk-is-what-extraction-sees.md @@ -1,12 +1,6 @@ # 0039 · A chunk is what extraction sees -- **Status**: Implemented, cut 1 · the parser's Markdown is read into top-level blocks - (`blocks.rs`), the packer works over blocks with a token budget (`chunker.rs`): a table - travels with its caption and header, a table too wide for one chunk is split by rows and every - continuation repeats caption and header, headings become a breadcrumb prefix and the - `chunks.heading` column, a table split by a page break is joined back · not in this cut: - an external parser behind the block model (cut 2, Docling for PDF layout and scans), evidence - that points at a table cell or an image region, a budget measured for its own sake +- **Status**: In progress · cut 1 built · open: cut 2, an external parser - **Written**: 2026-09-13 (conventions in the [README](README.md)) - **Related**: [0006](0006-ontology-scale-and-the-prompt.md) sets the other half of the prompt budget; [0033](0033-rss-source-summaries-are-source-scoped.md) is the recall bench's home; @@ -238,3 +232,9 @@ not the column names, which is the open question below. come out as tab- and pipe-separated lines rather than Markdown tables and are likewise read as paragraphs. Emitting real Markdown tables from those four parsers is cheap for the first two and is cut 2 for the last two. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented, cut 1 · the parser's Markdown is read into top-level blocks (`blocks.rs`), the packer works over blocks with a token budget (`chunker.rs`): a table travels with its caption and header, a table too wide for one chunk is split by rows and every continuation repeats caption and header, headings become a breadcrumb prefix and the `chunks.heading` column, a table split by a page break is joined back · not in this cut: an external parser behind the block model (cut 2, Docling for PDF layout and scans), evidence that points at a table cell or an image region, a budget measured for its own sake diff --git a/docs/decisions/0040-a-chunk-says-where-its-words-came-from.md b/docs/decisions/0040-a-chunk-says-where-its-words-came-from.md index 1026de842..1c92648a5 100644 --- a/docs/decisions/0040-a-chunk-says-where-its-words-came-from.md +++ b/docs/decisions/0040-a-chunk-says-where-its-words-came-from.md @@ -1,22 +1,6 @@ # 0040 · A chunk says where its words came from -- **Status**: Cut 1 implemented (2026-09-15) · `chunks.origin` / `origin_model` / `anchor` - with the anchor shape checked (migration 0058), the packer never mixes provenances - (`chunk_segments`), the ceiling on described facts, origin in the evidence API, the MCP - changes feed and the RDF export (`utopia:evidenceOrigin`) · a file that needs a reader no - longer becomes garbage text: images and recordings are recognised by header or extension, a - PDF with an empty text layer is a scan, and without the model the document fails once with - `documents.reader_needed` and a `document.needs_reader` alert · revised 2026-09-15: scans and - images are read by a MinerU service instead of a Docling sidecar, and a transcript must label - speakers · cut 2 implemented (2026-09-15): scans and images are read by a workspace's MinerU - service (`llm_settings.ocr_*`, migration 0059), one segment per page with the covered regions' - box in the anchor; the processing job waits on the service with `Deferred` and remembers the - remote task on the document; saving the service queues the waiting documents again · the - settings cards are their own interface cut · cut 3 implemented (2026-09-16): recordings are read - by a workspace's transcription model (`llm_settings.transcribe_*`, migration 0060) through - `/audio/transcriptions` with `diarized_json`; speakers are written into the text and a chunk's - anchor carries its times and speakers; a transcript without speaker labels degrades like a - missing model +- **Status**: Implemented · cuts 1–3, 2026-09-15 and 2026-09-16 (migrations 0058–0060) - **Written**: 2026-09-13 (conventions in the [README](README.md)) - **Related**: [0039](0039-a-chunk-is-what-extraction-sees.md) (#633, not merged yet) puts Docling behind the block model as its cut 2 and leaves "evidence that points at a table cell or an image @@ -268,3 +252,9 @@ the recording at `start_ms` — is a separate cut after the capability, not part | Route described facts to `pending_facts` (0015) | That queue waits for a nod on sentences a person said to the base. A single slide deck would bury those nods under hundreds of image facts, and a described fact is not anyone's assertion. | | Copy images out as blobs of their own | The original file already holds them, content-addressed and versioned. An anchor into it costs nothing and cannot drift. | | Reuse the chat model for vision and transcription | Sensitivity differs by modality, and one setting sends the most sensitive material wherever chat is hosted. | + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Cut 1 implemented (2026-09-15) · `chunks.origin` / `origin_model` / `anchor` with the anchor shape checked (migration 0058), the packer never mixes provenances (`chunk_segments`), the ceiling on described facts, origin in the evidence API, the MCP changes feed and the RDF export (`utopia:evidenceOrigin`) · a file that needs a reader no longer becomes garbage text: images and recordings are recognised by header or extension, a PDF with an empty text layer is a scan, and without the model the document fails once with `documents.reader_needed` and a `document.needs_reader` alert · revised 2026-09-15: scans and images are read by a MinerU service instead of a Docling sidecar, and a transcript must label speakers · cut 2 implemented (2026-09-15): scans and images are read by a workspace's MinerU service (`llm_settings.ocr_*`, migration 0059), one segment per page with the covered regions' box in the anchor; the processing job waits on the service with `Deferred` and remembers the remote task on the document; saving the service queues the waiting documents again · the settings cards are their own interface cut · cut 3 implemented (2026-09-16): recordings are read by a workspace's transcription model (`llm_settings.transcribe_*`, migration 0060) through `/audio/transcriptions` with `diarized_json`; speakers are written into the text and a chunk's anchor carries its times and speakers; a transcript without speaker labels degrades like a missing model diff --git a/docs/decisions/0041-a-name-is-a-claim-about-an-entity.md b/docs/decisions/0041-a-name-is-a-claim-about-an-entity.md index 5405633f5..7050dbbca 100644 --- a/docs/decisions/0041-a-name-is-a-claim-about-an-entity.md +++ b/docs/decisions/0041-a-name-is-a-claim-about-an-entity.md @@ -1,6 +1,6 @@ # 0041 · A name is a claim about an entity -- **Status**: decision 1 settled 2026-09-13 (names are facts) · cut 0 built: `scripts/bench/identity.mjs`, baseline on `dev` forward F1 0.43, reverse 0.54 · cut 1 implemented (#670): migration 0055 and `names` in the store, the extractor's `names`, shared-name pairs to the adjudicator; forward 0.68, reverse 0.68, the two orders agree on all 210 pairs (one run each; two cut-1 runs differed by 0.07 forward) · re-measured on DeepSeek-V3 after the review fixes: dev 0.46 / 0.40 (2 of 21 anchors unresolved), cut 1 0.61 / 0.44–0.53 over three runs; on V3 the adjudicator keeps 海洋探测器1号 and 海探1 apart in reverse order even with the shared name listed, which is cut 3's question · decisions 2 and 5 revised by cut 1 · cut 2 channel 2 (name vectors) built 2026-09-23: migration 0080 `name_vectors`, recall proposes a `name_vector|` pair for the adjudicator and never merges; revised 2026-09-25 (#889): a batch *same* on such a pair is never applied on its own, it takes the tool-using second look or goes to a person when that look cannot run; channel 3 (neighbours) and the retirement of `recall_keys` wait for the bench · cuts 3–4 not started +- **Status**: In progress · cut 1 built (#670), cut 2 channel 2 built 2026-09-23, revised 2026-09-25 (#889) · open: cut 2 channel 3, cuts 3–4 - **Written**: 2026-09-13 (conventions in the [README](README.md)) - **Related**: [0009](0009-no-type-is-a-type.md) made an undecided type an honest state, and [0016](0016-close-the-open-seams-before-cutting-new-ones.md) B3 let a declared `disjointWith` keep names apart; #270 stopped a namesake tie from being settled by candidate order and #331 let facts break it; #428 and [0025](0025-governance-reads-the-ledger-before-it-decides.md) moved duplicates through a queue an agent works; #582 and #583 made the extractor copy the words that name each side of a fact; [0037](0037-a-relation-carries-its-own-attributes.md) put attributes on edges. @@ -108,3 +108,9 @@ The corpus covers two namesakes at one company across documents; two namesakes i 4. Re-evaluation (decision 5); `name_shape` out of the gate and the generic suffix lists out of recall, each when the bench says so (decision 6). The entity panel's list of names with their sources and validity, and the evidence lines on a Review card, follow once cut 3 lands. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> decision 1 settled 2026-09-13 (names are facts) · cut 0 built: `scripts/bench/identity.mjs`, baseline on `dev` forward F1 0.43, reverse 0.54 · cut 1 implemented (#670): migration 0055 and `names` in the store, the extractor's `names`, shared-name pairs to the adjudicator; forward 0.68, reverse 0.68, the two orders agree on all 210 pairs (one run each; two cut-1 runs differed by 0.07 forward) · re-measured on DeepSeek-V3 after the review fixes: dev 0.46 / 0.40 (2 of 21 anchors unresolved), cut 1 0.61 / 0.44–0.53 over three runs; on V3 the adjudicator keeps 海洋探测器1号 and 海探1 apart in reverse order even with the shared name listed, which is cut 3's question · decisions 2 and 5 revised by cut 1 · cut 2 channel 2 (name vectors) built 2026-09-23: migration 0080 `name_vectors`, recall proposes a `name_vector|` pair for the adjudicator and never merges; revised 2026-09-25 (#889): a batch *same* on such a pair is never applied on its own, it takes the tool-using second look or goes to a person when that look cannot run; channel 3 (neighbours) and the retirement of `recall_keys` wait for the bench · cuts 3–4 not started diff --git a/docs/decisions/0042-the-chat-loop-is-a-runner-with-hooks.md b/docs/decisions/0042-the-chat-loop-is-a-runner-with-hooks.md index d3bac4f2a..3cf1b6eb4 100644 --- a/docs/decisions/0042-the-chat-loop-is-a-runner-with-hooks.md +++ b/docs/decisions/0042-the-chat-loop-is-a-runner-with-hooks.md @@ -1,9 +1,6 @@ # 0042 · The chat loop is a runner with hooks -- **Status**: Implemented (#548) · the loop is rig's runner (`rig-core` / `rig-agent` 0.42, no - default features) · policy is one `AgentHook` in `api/agent.rs` · the wire stays `LlmClient` - behind `api/rig_model.rs` · revised 2026-09-26 (#937: tool-control text is checked in every - turn, not only the boundary answer) +- **Status**: Implemented (#548) · revised 2026-09-26 (#937) - **Written**: 2026-09-13 (conventions in the [README](README.md)) - **Related**: #546 (the issue and its findings), #509 / #543 (the stall and the guard this replaces), #547 (the mark on answers that cite nothing), #631 (the empty-reply retry, moved @@ -150,3 +147,9 @@ A clean result on one frozen set is not a zero-failure guarantee. - A per-task model (`on_model_select`, #470) is available in the runner and not wired. - Choosing a different chat model per base is the product answer to the skip rate; it is configuration, not loop code. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented (#548) · the loop is rig's runner (`rig-core` / `rig-agent` 0.42, no default features) · policy is one `AgentHook` in `api/agent.rs` · the wire stays `LlmClient` behind `api/rig_model.rs` · revised 2026-09-26 (#937: tool-control text is checked in every turn, not only the boundary answer) diff --git a/docs/decisions/0043-every-review-queue-is-governed.md b/docs/decisions/0043-every-review-queue-is-governed.md index 132e61307..e61d22870 100644 --- a/docs/decisions/0043-every-review-queue-is-governed.md +++ b/docs/decisions/0043-every-review-queue-is-governed.md @@ -1,6 +1,6 @@ # 0043 · Every review queue is governed -- **Status**: Decided 2026-09-14 · **no code on `dev`** · cut 1 was built in PR #699 — migration 0058 widening `agent_decisions` to `fact` and `conflict` with `summary` and `detail`, `queue_agent` walking low-confidence and stale facts then temporal conflicts after the duplicate rounds of the same `govern` job, every applied action revertible from the Agent queue — and that PR was **closed on 2026-09-17**, to re-land on the open graph once [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) cut 2 has settled alignment · violations, ontology defects and concept mappings are cut 2 · **the decisions below stand; only the code was withdrawn** +- **Status**: Accepted 2026-09-14 · no code on `dev` (#699 closed) · open: re-land on the open graph - **Restored to `dev` 2026-09-20.** The file left `dev` with PR #699 and its number stayed allocated and cited — [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) links to it, `docs/design/governance.md` and `docs/design/time.md` reason from its decisions 5 and 1, and `docs/design/README.md` has carried it as `proposed` throughout. A record is the reasoning, and the reasoning was never withdrawn, so the file belongs here whatever happened to the branch. The status line above is the only thing rewritten. - **Written**: 2026-09-14 (conventions in the [README](README.md)) - **Related**: [0025](0025-governance-reads-the-ledger-before-it-decides.md) (the governor this extends; its open question "Other queues"), [0026](0026-a-decision-records-why.md) (a person's why becomes a precedent), [0027](0027-an-automatic-merge-is-gated-by-what-it-can-undo.md) (act on what can be undone), [0022](0022-an-unknown-date-is-not-an-open-one.md) (the temporal engine whose conflicts this settles), [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) (nods, which stay with people), #695 @@ -68,3 +68,9 @@ Agent rows for facts and conflicts carry their summary in place of two names. A - **Cut 2: violations, ontology defects, concept mappings.** A violation's `fact_retracted` and `fact_closed` fit this shape. `axiom_relaxed` changes the ontology, and `fixed` for a defect means a person changed it; both need a gate before an agent applies them. - **The second look.** The fact and conflict queues have no tool loop yet (0025 decision 5); the batch sees the evidence directly. Add one when proposals keep asking for something the batch could not see. - **The interface.** The Agent queue shows these rows with minimal changes; the queue cards themselves don't yet show the agent's proposal inline, as duplicate cards do. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Decided 2026-09-14 · **no code on `dev`** · cut 1 was built in PR #699 — migration 0058 widening `agent_decisions` to `fact` and `conflict` with `summary` and `detail`, `queue_agent` walking low-confidence and stale facts then temporal conflicts after the duplicate rounds of the same `govern` job, every applied action revertible from the Agent queue — and that PR was **closed on 2026-09-17**, to re-land on the open graph once [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) cut 2 has settled alignment · violations, ontology defects and concept mappings are cut 2 · **the decisions below stand; only the code was withdrawn** diff --git a/docs/decisions/0044-the-ontology-is-a-view-over-what-documents-say.md b/docs/decisions/0044-the-ontology-is-a-view-over-what-documents-say.md index 78501c07f..a46377ff0 100644 --- a/docs/decisions/0044-the-ontology-is-a-view-over-what-documents-say.md +++ b/docs/decisions/0044-the-ontology-is-a-view-over-what-documents-say.md @@ -1,6 +1,6 @@ # 0044 · The ontology is a view over what documents say -- **Status**: Accepted 2026-09-17 · cut 1 built: extraction writes open statements (#731), memory documents take the same path (#735), the typed path is deleted (#736), kind words bind to classes with two votes (#741), the contract's rules for things, phrases, tables and moods (#743, #744, #745) · cut 2 in progress: bindings (#751), materialisation (0067, 0068), the human decision with its job (0051, #876), the decision basis (0053, #878), **implication rules with cached readings (migration 0073, PR #882)** — the parity run against the bound pass waits for the typed-graph bench (#880) to be run with a model; identity profiles (cut 4) in progress in a separate track; **the errata agent (cut 6) built: structural flags, a JSON action protocol with a per-document budget, actions through the 0027 gate (migration 0074, PR #885)** — its measure (precision gained against correct facts removed, tokens) is the `--errata` run of the typed-graph bench, not yet run with a model · current state in [design/extraction](../design/extraction.md) and [design/ontology](../design/ontology.md) · replaces the staged-reading draft of this record (skim card, graded mentions, statements bound at write time), which the prototype below did not bear out · prototype scripts and measurements from 2026-09-15 are summarised in [What the prototype measured](#what-the-prototype-measured) +- **Status**: In progress · accepted 2026-09-17 · cut 1 built (#731), cut 2 mostly built (#751, #876, #878, #882), cut 5 is 0061, cut 6 built (#885) · open: the parity and errata bench runs, cut 4 - **Written**: 2026-09-16 (conventions in the [README](README.md)) - **Related**: [0022](0022-an-unknown-date-is-not-an-open-one.md) put a document's date in `attested_at` beside the world and record axes; [0025](0025-governance-reads-the-ledger-before-it-decides.md) and [0027](0027-an-automatic-merge-is-gated-by-what-it-can-undo.md) put agent decisions through a gate that weighs what they can undo; [0041](0041-a-name-is-a-claim-about-an-entity.md) made names facts and identity a matter of evidence; [0043](0043-every-review-queue-is-governed.md) sent every review queue through the governor; #714 found upload time used as the document date in extraction. @@ -139,3 +139,9 @@ Today's extractor stays as it is until cut 2 passes its thresholds. - Whether derivation rules recover the implicit facts that write-time binding found (Re-DocRED's country and located-in relations are a fifth of its pairs). - Competency questions for a new knowledge base that has none yet. - How much of identity the deterministic evidence settles before the adjudicator is needed. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Accepted 2026-09-17 · cut 1 built: extraction writes open statements (#731), memory documents take the same path (#735), the typed path is deleted (#736), kind words bind to classes with two votes (#741), the contract's rules for things, phrases, tables and moods (#743, #744, #745) · cut 2 in progress: bindings (#751), materialisation (0067, 0068), the human decision with its job (0051, #876), the decision basis (0053, #878), **implication rules with cached readings (migration 0073, PR #882)** — the parity run against the bound pass waits for the typed-graph bench (#880) to be run with a model; identity profiles (cut 4) in progress in a separate track; **the errata agent (cut 6) built: structural flags, a JSON action protocol with a per-document budget, actions through the 0027 gate (migration 0074, PR #885)** — its measure (precision gained against correct facts removed, tokens) is the `--errata` run of the typed-graph bench, not yet run with a model · current state in [design/extraction](../design/extraction.md) and [design/ontology](../design/ontology.md) · replaces the staged-reading draft of this record (skim card, graded mentions, statements bound at write time), which the prototype below did not bear out · prototype scripts and measurements from 2026-09-15 are summarised in [What the prototype measured](#what-the-prototype-measured) diff --git a/docs/decisions/0045-a-time-mention-is-resolved-against-its-document.md b/docs/decisions/0045-a-time-mention-is-resolved-against-its-document.md index 955563391..d7aa43d23 100644 --- a/docs/decisions/0045-a-time-mention-is-resolved-against-its-document.md +++ b/docs/decisions/0045-a-time-mention-is-resolved-against-its-document.md @@ -1,6 +1,6 @@ # 0045 · A time mention is resolved against its document -- **Status**: Accepted 2026-09-17 · cuts 1, 2 and 3 built (#740, #761): a document is dated from its own text, each time mention is interpreted by the model and computed by code, upload time is used nowhere, and a timeline closes on how a start was got rather than on a confidence number · cut 4 (re-resolution when a person sets a document's date, the time-anchor review queue) not built · current state in [design/time](../design/time.md) · expands decision 5 of [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) and revises decision 3 of [0022](0022-an-unknown-date-is-not-an-open-one.md) for undated documents · #714 closed +- **Status**: In progress · accepted 2026-09-17 · cuts 1–3 built (#740, #761) · open: cut 4 - **Written**: 2026-09-16 (conventions in the [README](README.md)) - **Related**: [0019](0019-the-second-clock-can-be-rewound.md) and [0022](0022-an-unknown-date-is-not-an-open-one.md) gave facts a world axis, a record axis and an attestation; [0024](0024-the-world-axis-reaches-the-second.md) fixed the precision ladder; [0031](0031-an-event-holds-at-the-moment-it-names.md) made a predicate's temporal kind normalise what is written; #679 made a timeline independent of arrival order; #680 read the opening of a document into every chunk; #688 accepted spelled-out dates; #714 found upload time used as the document date; [0041](0041-a-name-is-a-claim-about-an-entity.md) is the pattern this record copies for time. @@ -96,3 +96,9 @@ Thresholds before cut 2 lands: mention normalisation at or above 95% on absolute - A document that defines more than one calendar (a company's fiscal year and a subsidiary's). - Whether a period-valued attribute (a figure for a quarter) is one statement with an interval or a value with a period mention; 0031's event bucket suggests the former. - Time zones for events stated with a clock time and no zone (0024 folds them to the day). + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Accepted 2026-09-17 · cuts 1, 2 and 3 built (#740, #761): a document is dated from its own text, each time mention is interpreted by the model and computed by code, upload time is used nowhere, and a timeline closes on how a start was got rather than on a confidence number · cut 4 (re-resolution when a person sets a document's date, the time-anchor review queue) not built · current state in [design/time](../design/time.md) · expands decision 5 of [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) and revises decision 3 of [0022](0022-an-unknown-date-is-not-an-open-one.md) for undated documents · #714 closed diff --git a/docs/decisions/0046-the-app-surface-is-mcp.md b/docs/decisions/0046-the-app-surface-is-mcp.md index a6ab03da2..7790dc5f5 100644 --- a/docs/decisions/0046-the-app-surface-is-mcp.md +++ b/docs/decisions/0046-the-app-surface-is-mcp.md @@ -1,7 +1,6 @@ # 0046 · The app surface is MCP -- **Status**: Decided 2026-09-19 · no app center, no component runtime, no sandbox. The design that - was refused is kept below so the question is not reopened from nothing +- **Status**: Accepted 2026-09-19 · a refusal: no app center, no component runtime, no sandbox - **Written**: 2026-09-19 (conventions in the [README](README.md)) - **Related**: [0014](0014-identity-from-the-person-scope-from-the-token.md) and [0020](0020-an-auditor-reads-it-without-us.md) are the surface this record points at; diff --git a/docs/decisions/0047-a-rule-may-conclude-a-relation.md b/docs/decisions/0047-a-rule-may-conclude-a-relation.md index db8d3631f..18c79af1c 100644 --- a/docs/decisions/0047-a-rule-may-conclude-a-relation.md +++ b/docs/decisions/0047-a-rule-may-conclude-a-relation.md @@ -1,6 +1,6 @@ # 0047 · A rule may conclude a relation -- **Status**: Implemented in #861 (migration 0071), 2026-09-25 · caps unchanged, decision 4's measurement is in the PR · revises the edge exclusion stated in [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md), and asks the question [0030](0030-a-rule-may-read-what-a-rule-concluded.md) parked as "nobody has asked it" +- **Status**: Implemented 2026-09-25 (#861, migration 0071) - **Written**: 2026-09-20 (conventions in the [README](README.md)) - **Related**: [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md) built the rule and excluded an edge conclusion; [0030](0030-a-rule-may-read-what-a-rule-concluded.md) replaced that exclusion's acyclicity argument with a finiteness one on the value channel, and this record carries it to the edge channel; **[0032](0032-a-rule-computes-what-it-concludes.md) already decided that a rule may reach a value across one relation** and has not built it — this record depends on that loader rather than re-deciding it; [0002](0002-reasoning-engine.md) built the axiom fixed point and left R3 open; [0024](0024-the-world-axis-reaches-the-second.md) governs the precision of a derived bound; [0013](0013-a-source-should-hand-over-its-history.md) forbids reading a previous run's output, which stays forbidden. From #818. @@ -66,3 +66,9 @@ Schema and model; the single-hop edge channel 0032 also needs; the evaluator wit - **A pair `(X, Y)` reachable by several join edges with different intervals.** Each edge is its own premise set and therefore its own conclusion row, the same way two readings are today [0032]. Worth confirming against a real corpus that it does not multiply rows past usefulness. - **Whether the join may run backwards** as `Y --join--> X` without a declared inverse. Declaring `inverse_of` already expresses it and keeps one direction in the model, which argues for refusing the sugar. - **Chaining through a concluded predicate is available two ways** once decision 3 holds: another rule joining on it, or the predicate declared transitive so the axiom pass extends it. They produce different proofs for the same conclusion, and nothing here says which one a person should reach for. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Implemented in #861 (migration 0071), 2026-09-25 · caps unchanged, decision 4's measurement is in the PR · revises the edge exclusion stated in [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md), and asks the question [0030](0030-a-rule-may-read-what-a-rule-concluded.md) parked as "nobody has asked it" diff --git a/docs/decisions/0048-provenance-references-stay-inside-the-knowledge-base.md b/docs/decisions/0048-provenance-references-stay-inside-the-knowledge-base.md index 4a6b5d836..fc1a24ff4 100644 --- a/docs/decisions/0048-provenance-references-stay-inside-the-knowledge-base.md +++ b/docs/decisions/0048-provenance-references-stay-inside-the-knowledge-base.md @@ -1,6 +1,6 @@ # 0048 · Provenance references stay inside the knowledge base -- **Status**: Implemented in PR #832 (migration 0070) +- **Status**: Implemented (#832, migration 0070) - **Written**: 2026-09-20 (conventions in the [README](README.md)) - **Related**: [0009](0009-no-type-is-a-type.md)'s "NULL means undecided" is why several edges below are nullable and therefore cannot lean on `MATCH FULL`; [0002](0002-reasoning-engine.md) owns the derivation model whose premise edges are covered here. Mechanism choice resolved by PR #832; discussion tracked in issue #842. diff --git a/docs/decisions/0049-expression-declarations-are-checked-when-a-rule-is-written.md b/docs/decisions/0049-expression-declarations-are-checked-when-a-rule-is-written.md index 30cc060bd..63febc064 100644 --- a/docs/decisions/0049-expression-declarations-are-checked-when-a-rule-is-written.md +++ b/docs/decisions/0049-expression-declarations-are-checked-when-a-rule-is-written.md @@ -1,6 +1,6 @@ # 0049 · Expression declarations are checked when a rule is written -- **Status**: proposed; domain contract pending review. The opt-in web draft does not validate units or save rules. +- **Status**: Proposed - **Written**: 2026-09-21 - **Related**: [0032](0032-a-rule-computes-what-it-concludes.md); [PR #839](https://github.com/deeplethe/utopia/pull/839). diff --git a/docs/decisions/0050-an-action-attempt-keeps-its-identity-and-uncertain-outcome.md b/docs/decisions/0050-an-action-attempt-keeps-its-identity-and-uncertain-outcome.md index d543bf5ee..96528cb6e 100644 --- a/docs/decisions/0050-an-action-attempt-keeps-its-identity-and-uncertain-outcome.md +++ b/docs/decisions/0050-an-action-attempt-keeps-its-identity-and-uncertain-outcome.md @@ -1,6 +1,6 @@ # 0050 · An action attempt keeps its identity and uncertain outcome -- **Status**: proposed; domain contract pending review. Documentation only; no production schema, sender or routes. +- **Status**: Proposed · documentation only - **Written**: 2026-09-21 - **Related**: [0034](0034-an-action-is-a-declared-call.md); [PR #840](https://github.com/deeplethe/utopia/pull/840). diff --git a/docs/decisions/0051-a-human-phrase-decision-carries-its-materialization-work.md b/docs/decisions/0051-a-human-phrase-decision-carries-its-materialization-work.md index aa3f0a5b7..7623a6f59 100644 --- a/docs/decisions/0051-a-human-phrase-decision-carries-its-materialization-work.md +++ b/docs/decisions/0051-a-human-phrase-decision-carries-its-materialization-work.md @@ -1,6 +1,6 @@ # 0051 · A human phrase decision carries its materialization work -- **Status**: implemented 2026-09-23 on the production path (PR #876; the store refactors and regressions landed first in #841) · job kind `materialize_typed` registered in `main`, `phrase_bindings::decide_with_delivery` commits the decision and the job in one transaction, the phrase route answers `202` with the job id and no invented `typed` counts, `GET /kbs/{id}/jobs/{job_id}` is the authorized status read, completion reaches the page as the existing `review` / `graph` events, Review copy in both languages · the synchronous human entry point with a 2-second lock budget (#864, same day) is retired on this route as a consequence: the route no longer waits for the lock at all, so there is nothing left to bound; the kind-word route's #828 timeout is untouched +- **Status**: Implemented 2026-09-23 (#876) - **Written**: 2026-09-21 - **Related**: [0044](0044-the-ontology-is-a-view-over-what-documents-say.md); [PR #841](https://github.com/deeplethe/utopia/pull/841). @@ -93,3 +93,9 @@ an unknown kind. Keep failed work visible; never mark outstanding jobs done just make rollback clean. External actions (#530) must not use this retry/recovery path. **Revision 2026-09-23 (implementation).** The "asynchronous HTTP/job/UI contract" this record left open is now the shape above. Two choices the record left to the implementation: the job's Busy outcome retries after 10 seconds through the existing `Deferred` path and its bounded window, and the status read is scoped by the job payload's `kb_id` so a job of another base answers 404 like an invisible document. The materialization job also writes an `alignment.materialized` audit row when the projection changed, with the job id, so a person can tie a click to what it did once the event has passed. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented 2026-09-23 on the production path (PR #876; the store refactors and regressions landed first in #841) · job kind `materialize_typed` registered in `main`, `phrase_bindings::decide_with_delivery` commits the decision and the job in one transaction, the phrase route answers `202` with the job id and no invented `typed` counts, `GET /kbs/{id}/jobs/{job_id}` is the authorized status read, completion reaches the page as the existing `review` / `graph` events, Review copy in both languages · the synchronous human entry point with a 2-second lock budget (#864, same day) is retired on this route as a consequence: the route no longer waits for the lock at all, so there is nothing left to bound; the kind-word route's #828 timeout is untouched diff --git a/docs/decisions/0052-document-content-is-a-read-contract.md b/docs/decisions/0052-document-content-is-a-read-contract.md index 2860fe108..a53c45597 100644 --- a/docs/decisions/0052-document-content-is-a-read-contract.md +++ b/docs/decisions/0052-document-content-is-a-read-contract.md @@ -1,6 +1,6 @@ # 0052 · Document content is a read contract over the retained ledger -- **Status**: proposed for review +- **Status**: Proposed - **Written**: 2026-09-21 - **Related**: [#859](https://github.com/deeplethe/utopia/issues/859); [0014](0014-identity-from-the-person-scope-from-the-token.md); [0040](0040-a-chunk-says-where-its-words-came-from.md) diff --git a/docs/decisions/0053-a-phrase-decision-records-the-inputs-it-considered.md b/docs/decisions/0053-a-phrase-decision-records-the-inputs-it-considered.md index b77056555..f239dc241 100644 --- a/docs/decisions/0053-a-phrase-decision-records-the-inputs-it-considered.md +++ b/docs/decisions/0053-a-phrase-decision-records-the-inputs-it-considered.md @@ -1,6 +1,6 @@ # 0053 · A phrase decision records the inputs it considered -- **Status**: implemented 2026-09-23 in PR #878 · `phrase_bindings.basis` (migration 0072), candidates admitted through the class hierarchy and shown to the model as such, structural outcomes recorded instead of skipped, the requeue condition reads live signatures only · closes the lifecycle half of #807 and the phrase half of #795 · kind words since 2026-09-26: `type_bindings.basis` (migration 0092), read from one snapshot and compared again when a reply is accepted, see [the revision below](#revision-2026-09-26-kind-words) +- **Status**: Implemented 2026-09-23 (#878, migration 0072) · revised 2026-09-26 (migration 0092) - **Written**: 2026-09-23 - **Related**: [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) decision 3; [0051](0051-a-human-phrase-decision-carries-its-materialization-work.md); #807, #795, #801 (withdrawn), #773, #754 @@ -143,3 +143,9 @@ whose batch fails takes the bounded re-ask; a failed retrieval falls back and is while it keeps failing; a person's decision carries no basis, is not overwritten, and commits with its job; an acceptance and a class delete wait for each other instead of deadlocking, and a candidate deleted before the check turns the reply away. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented 2026-09-23 in PR #878 · `phrase_bindings.basis` (migration 0072), candidates admitted through the class hierarchy and shown to the model as such, structural outcomes recorded instead of skipped, the requeue condition reads live signatures only · closes the lifecycle half of #807 and the phrase half of #795 · kind words since 2026-09-26: `type_bindings.basis` (migration 0092), read from one snapshot and compared again when a reply is accepted, see [the revision below](#revision-2026-09-26-kind-words) diff --git a/docs/decisions/0054-a-source-may-push-statements-in-the-open-contract.md b/docs/decisions/0054-a-source-may-push-statements-in-the-open-contract.md index d48e13f25..14e31a288 100644 --- a/docs/decisions/0054-a-source-may-push-statements-in-the-open-contract.md +++ b/docs/decisions/0054-a-source-may-push-statements-in-the-open-contract.md @@ -1,6 +1,6 @@ # 0054 · A source may push statements in the open contract -- **Status**: proposed · cut 1 in this record's PR: the `statements` source kind, `POST /sources/{id}/statements`, deterministic extraction, the Library entry · no schema change +- **Status**: Implemented · cut 1 (#884) - **Written**: 2026-09-23 - **Related**: [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) owns the contract this reuses and the rule that typed facts come only from alignment; [0001](0001-extraction.md) is why every statement has evidence; [0022](0022-a-fact-has-two-clocks.md) is the two clocks a pushed item lands on; [0036](0036-exploration-aligns-a-schema-to-the-ontology.md) is where structured *state* lives, which this record leaves alone; [0015](0015-recording-a-sentence-is-not-asserting-a-fact.md) is why a person's `remember` needs a nod and a source's document does not; #875 is the case that surfaced it. diff --git a/docs/decisions/0060-a-rule-definition-has-a-history.md b/docs/decisions/0060-a-rule-definition-has-a-history.md index d3888aeda..a9f14379c 100644 --- a/docs/decisions/0060-a-rule-definition-has-a-history.md +++ b/docs/decisions/0060-a-rule-definition-has-a-history.md @@ -1,6 +1,6 @@ # 0060 · A rule's definition has a history -- **Status**: implemented 2026-09-25 in the PR for #912 · `attribute_rule_versions` (migration 0076), a derivation names the version it was drawn under, the proof and the rules panel read it, `GET /kbs/{id}/rules/{rule_id}/versions` · the export of business-rule bodies that [0020](0020-an-auditor-reads-it-without-us.md)'s revision deferred can now follow +- **Status**: Implemented 2026-09-25 (#913, migration 0076) - **Written**: 2026-09-25 (conventions in the [README](README.md)) - **Related**: [0002](0002-reasoning-engine.md) made a derivation keep its record-time lifetime; [0019](0019-the-second-clock-can-be-rewound.md) is the record axis this record extends to rules; [0021](0021-a-rule-reads-attributes-and-concludes-a-type.md) built the business rule as one row; [0030](0030-a-rule-may-read-what-a-rule-concluded.md) keeps a kept conclusion's row and reproves it; [0020](0020-an-auditor-reads-it-without-us.md) (revision 2026-09-25) declined to export rule bodies for the reason this record removes. From #912, out of #902. @@ -25,3 +25,9 @@ - **The export of a version's body.** This record makes it honest to export a business rule's conditions and expressions per version, which 0020's revision deferred; the vocabulary for operands, range bounds and expressions is still #902's second cut and is not chosen here. - **Restoring an old version.** Editing back to an earlier definition opens a new version with the same content. A "revert" button would be sugar over that and can wait for someone to want it. - **Versions of axiom declarations.** An axiom is a flag on a predicate, and a derivation already names the declaring predicate and kind; whether declarations need a history is a different question. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> implemented 2026-09-25 in the PR for #912 · `attribute_rule_versions` (migration 0076), a derivation names the version it was drawn under, the proof and the rules panel read it, `GET /kbs/{id}/rules/{rule_id}/versions` · the export of business-rule bodies that [0020](0020-an-auditor-reads-it-without-us.md)'s revision deferred can now follow diff --git a/docs/decisions/0061-the-ontology-is-proposed-from-the-open-graph-and-judged-by-its-questions.md b/docs/decisions/0061-the-ontology-is-proposed-from-the-open-graph-and-judged-by-its-questions.md index a49ce8007..947e0d053 100644 --- a/docs/decisions/0061-the-ontology-is-proposed-from-the-open-graph-and-judged-by-its-questions.md +++ b/docs/decisions/0061-the-ontology-is-proposed-from-the-open-graph-and-judged-by-its-questions.md @@ -1,6 +1,6 @@ # 0061 · The ontology is proposed from the open graph and judged by its questions -- **Status**: Accepted 2026-09-26 · 0044 cut 5 · cut 1 built (#947, migration 0077): competency questions are rows, the agent proposes from unbound signatures and kind words into `ontology_proposals`, adoption writes the element and re-decides its shapes through alignment · cut 1.1 (#950, migration 0078): the agent records every shape it saw, "already in the ontology" becomes a map_to proposal adopted as a person's binding per shape, adoption embeds the element before alignment, and the editor's create and update handlers queue the same refresh ahead of alignment, which waits for it · measured on a 100-document base: ~9k tokens a call, 12 calls a round, about half of a round's proposals adoptable as-is · cut 3 first slice (#955, migration 0079): the agent proposes questions for a base with none, `competency.mjs` asks the accepted ones through chat and the two numbers are reported (11 of 11 on the bench base) · second slice (#962): each answer records whether it went through the graph; on the bench base 10 of 11 answered, 8 with graph facts, 6 from the graph alone · cut 2, the rest of cut 3 (FDA and statistics corpora) and cut 4 open · the ontology agent reads the open graph's unbound signatures and kind words against the base's competency questions and proposes types, properties and rules with definitions, examples and the signatures they would bind; approval writes the ontology and re-decides those signatures; every approved element carries regression cases; the ontology is measured by the questions it answers +- **Status**: In progress · accepted 2026-09-26 · cuts 1 and 1.1 built (#947, #950), cut 3 slices 1–2 (#955, #962) · open: cut 2, the rest of cut 3, cut 4 - **Written**: 2026-09-26 (conventions in the [README](README.md)) - **Related**: [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) §4 decided the workbench and named this cut; [0003](0003-ontology-growth-loop.md) grew the ontology from `ontology_misses` and surface predicates, which [0044](0044-the-ontology-is-a-view-over-what-documents-say.md) cut 1 left idle and #725 retires; [0012](0012-the-ontology-is-a-contract-not-a-suggestion.md) makes every element a contract a person signed, which this record keeps; [0053](0053-a-phrase-decision-records-the-inputs-it-considered.md) is what makes approval re-decide signatures without a new mechanism; [0008](0008-ontology-packs-as-cold-start.md) is the second of the three sources; [0043](0043-every-review-queue-is-governed.md) and #725 give the queue its shape. From #725's "Ontology proposals" row and 0044's open question about a base with no questions. @@ -49,3 +49,9 @@ The open graph holds statements with their phrases, quotes, kind words and names - What "answered correctly" means for a question with no expected answer written: the shape check is weaker than a judged answer, and a question that the open graph alone can answer does not need the ontology at all. - Proposals against an imported pack: a proposed property that a pack already has under another name should become a `map_to`, and the agent needs the pack's definitions to see it. - Whether a rejected proposal's reason should suppress the element for a time or until the signatures behind it change. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> Accepted 2026-09-26 · 0044 cut 5 · cut 1 built (#947, migration 0077): competency questions are rows, the agent proposes from unbound signatures and kind words into `ontology_proposals`, adoption writes the element and re-decides its shapes through alignment · cut 1.1 (#950, migration 0078): the agent records every shape it saw, "already in the ontology" becomes a map_to proposal adopted as a person's binding per shape, adoption embeds the element before alignment, and the editor's create and update handlers queue the same refresh ahead of alignment, which waits for it · measured on a 100-document base: ~9k tokens a call, 12 calls a round, about half of a round's proposals adoptable as-is · cut 3 first slice (#955, migration 0079): the agent proposes questions for a base with none, `competency.mjs` asks the accepted ones through chat and the two numbers are reported (11 of 11 on the bench base) · second slice (#962): each answer records whether it went through the graph; on the bench base 10 of 11 answered, 8 with graph facts, 6 from the graph alone · cut 2, the rest of cut 3 (FDA and statistics corpora) and cut 4 open · the ontology agent reads the open graph's unbound signatures and kind words against the base's competency questions and proposes types, properties and rules with definitions, examples and the signatures they would bind; approval writes the ontology and re-decides those signatures; every approved element carries regression cases; the ontology is measured by the questions it answers diff --git a/docs/decisions/0062-the-export-says-what-is-contested.md b/docs/decisions/0062-the-export-says-what-is-contested.md index 000514c58..f42ac9063 100644 --- a/docs/decisions/0062-the-export-says-what-is-contested.md +++ b/docs/decisions/0062-the-export-says-what-is-contested.md @@ -1,6 +1,6 @@ # 0062 · The export says what is contested -- **Status**: accepted 2026-09-26 (#922) · implements [#564](https://github.com/deeplethe/utopia/issues/564) · precondition: the current-ontology invariant described under [The `open` invariant](#the-open-invariant) +- **Status**: Implemented 2026-09-26 (#922, migration 0093) - **Written**: 2026-09-25 (conventions in the [README](README.md)) - **Related**: [0020](0020-an-auditor-reads-it-without-us.md) (the export this extends); [0012](0012-the-ontology-is-a-contract-not-a-suggestion.md) (axiom violations); [0017](0017-a-contradiction-points-upstream.md) (`derived_contradiction`); [#550](https://github.com/deeplethe/utopia/issues/550) (the export is the supported machine-readable read contract); #612, #613, #618, #619 (the storage-side findings this record had to respect) @@ -109,3 +109,9 @@ None of this enters the contract, now or later, just because it exists in storag ## Vocabulary added Everything without a standard spelling mints under `urn:utopia:ns:`: the classes `FactConflict` and `AxiomViolation`; the links `priorStatement`, `incomingStatement`, `onStatement`, `evidencePath`, `onRelation`, `criterion`; and the literals `reason`, `kind`, `status`, `resolution`, `detectedAt`, `closedAt`. The value sets above are the contract: readers should treat unknown values as a newer contract, not silently. + +## Status history + +The status line as it stood on 2026-09-27, before status lines were cut to one line: + +> accepted 2026-09-26 (#922) · implements [#564](https://github.com/deeplethe/utopia/issues/564) · precondition: the current-ontology invariant described under [The `open` invariant](#the-open-invariant) diff --git a/docs/decisions/0063-stopping-a-chat-ends-the-generation.md b/docs/decisions/0063-stopping-a-chat-ends-the-generation.md index bab2aeb16..45b83d1bd 100644 --- a/docs/decisions/0063-stopping-a-chat-ends-the-generation.md +++ b/docs/decisions/0063-stopping-a-chat-ends-the-generation.md @@ -1,6 +1,6 @@ # 0063 · Stopping a chat ends the generation -- **Status**: Implemented 2026-09-27 ([#961](https://github.com/deeplethe/utopia/pull/961), migration 0095) +- **Status**: Implemented 2026-09-27 (#969, migration 0095) - **Written**: 2026-09-26 (conventions in the [README](README.md)) - **Related**: [#934](https://github.com/deeplethe/utopia/issues/934), [0042](0042-the-chat-loop-is-a-runner-with-hooks.md) diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 439eaeabf..bb80e3625 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -18,135 +18,138 @@ The test for writing one: if someone (including us) looks at a piece of code in **The PR that implements a record updates its status line.** Three status lines once lagged behind the code in the same PR, written by the same person. So the PR description answers one question: which record does this change implement or overturn, and is its status line updated. (Added 2026-09-02, from [0016](0016-close-the-open-seams-before-cutting-new-ones.md).) +**A status line is one line.** It starts with one of five words, `Proposed`, `Accepted`, `In progress`, `Implemented` or `Superseded by NNNN`, then the date and the PR or migration, then `open:` and what is not built. What a cut contains belongs in the record's body or in the design file, not in the status line. The index below repeats only the first word, so a PR touches this README only when that word changes. (Added 2026-09-27; the longer lines that stood before are kept at the end of each record under Status history.) + **Line numbers drift and file names change.** Prefer function, table and constant names over `file.rs:123`. Migrations were consolidated from 53 files into one per domain (#130, #131); older references to migration files are by domain. **Language: English.** The first sixteen records were written in Chinese and condensed into English on 2026-09-03; the Chinese originals remain in git history. Code comments are still Chinese; UI, README and records are English. ## Index -| | Record | Status | -|---|---|---| -| 0001 | [Ontology import and governance](0001-ontology-import-and-governance.md) | In progress · P0–P2c built; the P3 budget built, P3a by hand only; P3b built in a different shape; P4b / P4c pending; P5 delivered by 0002; criterion 2 half overturned by 0012 | -| 0002 | [Reasoning engine](0002-reasoning-engine.md) | R0 checker and R1 materialization (KB switch, default off) built; R2 proof chain (#227); contradiction signals per 0017; R3 incremental maintenance not built | -| 0003 | [The ontology grows out of the corpus](0003-ontology-growth-loop.md) | Built and running, default on · starting point rewritten by 0010 and the retired seeds · dismissal redone per 0007 · the "new phrasings" reminder pending | -| 0004 | [Language follows the reader of each text](0004-language-and-localization.md) | Built · UI strings, coded server errors, ontology description language and the locale of generated text · the browser language is not guessed yet | -| 0005 | [The alert center](0005-alert-center.md) | Built · five alert kinds live, search and paging in the panel · `document.no_text_layer` still unwired | -| 0006 | [Ontology scale and the extraction prompt](0006-ontology-scale-and-the-prompt.md) | Built · the character budget (24,000) and per-chunk retrieval live, values untested · the per-chunk list keeps to the same budget with first-sentence descriptions (#701) · answer keys still hand-filled | -| 0007 | [Counting decides what becomes a relation](0007-who-decides-what-becomes-a-relation.md) | Built · adoption decided by counting (`MIN_DOCS = 2`, `MIN_SIGNALS = 3`), proposals persist (#112) · narrative verbs and `_by` folding still open | -| 0008 | [Ontology packs as the cold start](0008-ontology-packs-as-cold-start.md) | Built · five packs embedded, multi-select at creation · no pack by default and none at registration (#580, measured) · three open questions stay open; Chinese labels got worse | -| 0009 | [An undecided type stays empty](0009-no-type-is-a-type.md) | Implemented · `type_id` nullable, builtin classes gone · kin classes go to Review (#226), declared `disjointWith` keeps them apart (0016 B3) · `metric` / `dimension` builtin on demand (#231) · `metric` / `dimension` to retire under 0036 | -| 0010 | [An unnamed relation stays empty](0010-no-relation-is-no-relation.md) | Implemented · `predicate_id` nullable, `related_to` gone, wording recovered by `fact_surface_predicate` · follow-ups done with 0011 | -| 0011 | [A mapping is configuration](0011-a-mapping-is-not-a-fact.md) | Implemented (#126 / #140 / #148) · Review flow and revision history rebuilt · the evidence chain not built · revised in part by 0036: the concept becomes an attribute or a rule, the table becomes a rendered one | -| 0012 | [The ontology is a contract](0012-the-ontology-is-a-contract-not-a-suggestion.md) | Implemented · violation rate 57% → 4%, reversals 39 → 0 · guard extended to adoption and merge (#190 / #196) · reified-shell filter at pack import open | -| 0013 | [A source hands over its history](0013-a-source-should-hand-over-its-history.md) | Implemented for GitHub, Jira and Notion (#134 / #135 / #213) · Feishu and Confluence not started · GitHub and Jira timestamps written to the second (#691) | -| 0014 | [Identity from the person, scope from the token](0014-identity-from-the-person-scope-from-the-token.md) | Implemented (#180) · five read-only MCP tools over Streamable HTTP · tokens page at `/account/tokens` · `can_write` still hard-coded false | -| 0015 | [A recorded sentence waits for a nod](0015-recording-a-sentence-is-not-asserting-a-fact.md) | Implemented · memory facts wait in `pending_facts`, nod queue and chat card, `remember` reopened · MCP write is the next cut | -| 0016 | [Close the open seams before cutting new ones](0016-close-the-open-seams-before-cutting-new-ones.md) | In progress · A done · B done (B4 deferred) · C1 done (#289) · C2 done (#297) · C3–C5 open · D2 worked around (#231); the lakehouse landed ahead of D4 (#239) | -| 0017 | [A contradiction points at an error upstream](0017-a-contradiction-points-upstream.md) | Implemented · B2a: engine and queue, per-item cap, aggregation by rule pair, cards with clues and repairs (#238) · B2b: contested edges in the alert colour, ghost edges for blocked derivations, the disputed chip and the "did not land" section in the panel (#243) | -| 0018 | [The lakehouse is one protocol away](0018-the-lakehouse-is-one-protocol-away.md) | Implemented: Trino (Iceberg / Delta / Hive), Databricks and Snowflake behind the same trait, scheme picks the engine (#239) · Trino verified against a real cluster (#327); Databricks and Snowflake still want one (#241, #242) · MaxCompute waits | -| 0019 | [The second clock can be rewound](0019-the-second-clock-can-be-rewound.md) | Implemented in three cuts · `held_at` and `as_of` on every graph read (#317), entities' own clock by unwinding `entity_merges` (#337), retrieval as of a moment · the entity panel and `entity_facts` rewind derivations too (#549) · the control on the graph page is still open (#307), full-text recall is still "now" only | -| 0020 | [An auditor reads it without us](0020-an-auditor-reads-it-without-us.md) | Implemented · revised 2026-09-25 (#902: a rule resource says its family, an axiom rule its kind and declaring predicate) · `GET /kbs/{id}/export?format=turtle\|jsonld` streams the base as RDF, `rdf.rs` holds the mapping · SPARQL waits, and the record says why (#308) | -| 0021 | [A rule reads attributes and concludes a type](0021-a-rule-reads-attributes-and-concludes-a-type.md) | Implemented (#359) · `derived_facts` widened to match `facts`, rules authored in `attribute_rules` from the ontology page, evaluated in the materialisation job, explained in the entity panel with their premises · read-only over MCP, writing a rule stays out · no canvas marker, and a conclusion is rewritten rather than edited | -| 0022 | [An unknown date is not an open one](0022-an-unknown-date-is-not-an-open-one.md) | Implemented in two cuts (#394 and the derived cut) · `world_axis` predicate beside `record_axis`, `facts.attested_at` anchors a missing start or an undated end at the document that attests it, every read and both client filters on the read interval, an undated ending closes the dated row it ends, derived rows intersect premise intervals as read and carry no precision on an anchored bound · two anchors (`attested_from` / `attested_to`), so a bare open row closes too (#393) · the temporal engine orders a start-less row by its earliest dated evidence and closes its predecessor as ended-unknown there; ends the engine drew are marked and recomputed from the rows a timeline has (0057); a relative deadline is stored as written (#679) | -| 0023 | [RSS observations are not documents](0023-rss-observations-are-not-documents.md) | Implemented in #326 | -| 0024 | [The world axis reaches the second](0024-the-world-axis-reaches-the-second.md) | Implemented · the precision ladder runs year → second (not below: no source states less), a stored value is truncated to its precision by CHECK on all three tables, a clock time without a zone is a date, one list spells the ladder in the extractor, the renderers, the export and the evaluator; a derived bound takes the precision of the premise that set it | -| 0025 | [Governance reads the ledger before it decides](0025-governance-reads-the-ledger-before-it-decides.md) | Cut 1 implemented · a per-base `governance` switch, a `govern` job that works the duplicates queue first in, first out with each head's cluster, precedents pulled from the ledger into the prompt, a gate where the agent's own confidence decides and history lowers the bar or blocks (revised after the first big-file run), every look a row in `agent_decisions`, answers through the person's own decide path · the switch, the Agent queue, the proposal chip and the Overview section in the UI · a pair the batch cannot settle is looked at again with tools and then decided or asked about, within a daily budget · two reverts in a week turn the switch off and raise an alert · identity rules the model reads and two it cannot argue with (name shapes, type families), measured against a hand-labeled set of 411 name pairs by `scripts/bench/govern.mjs` | -| 0026 | [A decision records why](0026-a-decision-records-why.md) | Implemented · `resolution_reviews.rationale` and `why` on the ledger event from every human decide path, precedents quote it into the prompt and the `ledger_search` tool, the batch adjudicator reads precedents and keys its cache on them, the one-in-ten sample for a person was removed 2026-09-14, the model's own why is kept beside machine decisions · the impact gate (#357) and the investigating adjudicator (#358) follow | -| 0027 | [An automatic merge is gated by what it can undo](0027-an-automatic-merge-is-gated-by-what-it-can-undo.md) | Implemented · `execution_gate` in the store, asked by the batch adjudicator and the governor before an automatic merge: a contradiction the consistency check would open (read on the time axis since 2026-09-14: a succession is not one), a derivation resting on either side, or an answer that named either side holds the pair for a person as `escalate_impact` whatever the confidence · keeps are not gated · exports and a per-deployment opt-in stay open | -| 0028 | [The adjudicator looks before it asks](0028-the-adjudicator-looks-before-it-asks.md) | Implemented · `consequences` joins the second look's tools (what a merge would touch, from 0027's gate, and whether the types share a family) · with governance off the batch adjudicator sends its unsettled pairs through the same loop under the same budget, each look a row in `agent_decisions` · the number that justifies or retires it comes from 0026's sample split by `via` | -| 0029 | [A rule may say "or", once](0029-a-rule-may-say-or-once.md) | Implemented · a `group_seq` on a rule's conditions (migration `0039`): same group joins with **and**, groups join with **or**, one level and no nesting — because a hit carries the readings that made it true and a disjunction has no premises of its own · the combination cap is per group, two groups on one interval are one conclusion with the first group's proof, `not_in` joins the ops · "no such attribute at all" is deliberately not here (no premise, and a missing reading means nobody wrote it down) · the editor writes blocks and the table reads the sentence back with its "or" in it | -| 0030 | [A rule may read what a rule concluded](0030-a-rule-may-read-what-a-rule-concluded.md) | Cuts 1–2 · A rule could not read another rule's conclusion (`attribute_facts()` reads `facts`, subject scope is `entities.type_id`, one pass), so `A → B → C` has to be written as one flattened rule. The answer is the fixed point the axiom side already runs, **inside one call and in memory** — which answers 0013's objection instead of waiving it, since a run stays a pure function of the asserted facts. A derived typing enters as a **premise**, not a filter, so the conclusion narrows to the window the entity was actually a `B`; a rule may read its own conclusions because the derivable space is finite; `fact_derivations` gains `premise_derived_id` and the proof becomes a tree; retirement cascades for free because recompute is total. The page showing which rules feed which is next | -| 0031 | [An event holds at the moment it names](0031-an-event-holds-at-the-moment-it-names.md) | Implemented (#486) · `Validity::under` normalises every write by the predicate's `temporal` (an event is one moment written at both ends, an eternal fact has no dates), `world_axis` and `read_span` read an event as the bucket it names and an undated event at no moment, an eternal fact at every moment · the prompt marks `[event]` / `[eternal]` and says what to write · no schema change, old rows read correctly · the panel's point rendering and the ontology hint are the UI cut | -| 0032 | [A rule computes what it concludes](0032-a-rule-computes-what-it-concludes.md) | Cuts 1–2 · A rule's conclusion was a **constant** (`attribute_rules.conclude_value` is a JSONB literal), so a criterion about a number nobody wrote down — net pay, a margin, a ratio — gets computed elsewhere and typed back in as an assertion, losing the readings it stood on. An operand on either side becomes an expression tree over attributes: its premises are the readings it touched and its interval is their intersection, so retirement needs no new machinery. **Picked, not typed** — not because a parsed string would break the display (it would not; that reason is wrong and recorded as wrong) but because a picker cannot compose an expression over a predicate that does not exist, and because a text box grows a grammar on request. A value may be reached **across a relation** (a path has definite premises and an interval; a to-many path is just a cartesian product the evaluator already walks), while **aggregation is refused**: a `sum` asserts *these are all the readings*, a completeness claim this base cannot hold — and while recompute still retires it correctly, its proof cannot say why it was withdrawn, and nothing incremental could ever wake it. Row count, unit checking and division by zero are the costs | -| 0033 | [RSS summaries are scoped to the source being listed](0033-rss-source-summaries-are-source-scoped.md) | Implemented · source- and generation-scoped lateral aggregation (#462), then the nested nullable `rss_full_content` contract replacing the eight flat columns (#417); `generation` and `baseline_count` stay internal, and baseline rows are intentionally outside the five work counts | -| 0034 | [An action is a declared call](0034-an-action-is-a-declared-call.md) | Cut 1 · the record. A base can conclude but cannot act: nothing holds a call it may make, a typed parameter, or what it sent. An action is **data** (method, URL, headers, a sealed auth block, a JSON body template, scalar parameters with bounds or a value set), substituted and never scripted, so the page renders what runs and an unknown placeholder is refused at save. Registered once for the deployment and **granted** to a base, one layer where warehouses have two because nothing is mounted. Every run is a row read by two log pages. The reach is the operator's, with no placeholder in the host. Rules fire actions in the next record | -| 0035 | [A vector index is built by a job](0035-a-vector-index-is-built-by-a-job.md) | Implemented · a partial HNSW index per dimension on `chunks.embedding` and `entities.profile_embedding`, requested by the first write of that dimension and built by a `build_vector_index` job outside any transaction · the two nearest-neighbour reads write the dimension as a literal, cast both sides and set `hnsw.iterative_scan = relaxed_order` · type resolution gathers its neighbours eight at a time in order and remembers descendant sets per batch (#512, #514) · dimensions above 2000 stay on the exact path | -| 0036 | [Exploration aligns a schema to the ontology](0036-exploration-aligns-a-schema-to-the-ontology.md) | Written, not implemented · exploration proposes an alignment per table (the class it is a table of, its attributes, its relations, a conversion tree per column) through `ontology_proposals`, adopted as one thing · a definition is a rule over aligned attributes, written by a person; a shared convention is one rule others read · conversions are 0032's expression tree, text parsed by `sqlparser`, never stored as SQL · `Metric` / `Dimension` retire and `concept_mappings` becomes rendered · the schema document leaves extraction · where a rule runs against the source stays open | -| 0037 | [A relation carries its own attributes](0037-a-relation-carries-its-own-attributes.md) | Written · cut 1 in progress: qualifier tables (0049), declaration, extraction, panel, export · entity-valued qualifier reserved, conflict-as-two-rows and the canvas label next | -| 0038 | [The interface has a light side](0038-the-interface-has-a-light-side.md) | Implemented (#599) · `data-theme` on ``, chosen by the reader and stored in the browser, `system` resolved to one of the two before first paint · a second token block in `styles.css` and not one rule rewritten: alpha stays, the triplet swaps · the canvas reads its colours from the tokens through one reader file and rebuilds on a switch; edges are flattened over the ground on light because the WebGL shader can only brighten · a `raw-colour` guard rule over `.ts` and `.tsx` · the entity palette is the same in both themes; a paper-tuned set is open | -| 0039 | [A chunk is what extraction sees](0039-a-chunk-is-what-extraction-sees.md) | Implemented, cut 1 · the parser's Markdown is read into blocks and packed by a token budget (1000, cl100k) · a table travels with its caption and header and a continuation repeats both · headings become a breadcrumb and fill `chunks.heading` · a page-break rule is not a boundary and a table it split is joined back · not measured on its own: the budget · cut 2: an external parser behind the block model, evidence that names a cell or a region | -| 0040 | [A chunk says where its words came from](0040-a-chunk-says-where-its-words-came-from.md) | Cuts 1–3 implemented (the ledger shape, and a file that needs a reader degrades with an alert instead of becoming garbage text; scans and images read by a workspace's MinerU service, one segment per page, waiting on the service without spending retries; recordings read by a diarizing transcription model, speakers in the text and times in the anchor, an unlabelled transcript degrades) · scans and images through a MinerU service, recordings only with speaker labels (revised 2026-09-15) · a chunk carries an **origin** (`stated`, `ocr`, `transcribed`, `described`) and the model that produced it, and an **anchor** back into the original bytes (a page and region, a time range, an image inside the file) — decided before any media reader, because a transcript stored without its times can never be tied to the recording again · the packer never mixes origins in a chunk · facts from a description enter below `AUTO_CLOSE_MIN_CONFIDENCE`, so a misread chart opens a conflict instead of closing a correct fact · per-modality model settings, media reading as a resumable job, origin and anchor in the API, MCP and RDF · cuts: ledger shape, scans via MinerU, recordings, descriptions, video | -| 0041 | [A name is a claim about an entity](0041-a-name-is-a-claim-about-an-entity.md) | Cut 0 built (identity bench) · cut 1 implemented (#670): names are value facts on `known_as`, the extractor reports other names, a shared name goes to the adjudicator; forward/reverse F1 0.43/0.54 → 0.68/0.68 · cuts 2–4 (name vectors and neighbours, evidence decides, re-evaluation) not started | -| 0042 | [The chat loop is a runner with hooks](0042-the-chat-loop-is-a-runner-with-hooks.md) | Implemented (#548) · the loop is rig's runner and every policy is a hook with a typed result · the wire stays `LlmClient` behind `RigModel` · a turn cannot end before a tool has run, `no_evidence_needed` is the exit for questions not about the base · RAG fallback only on a 400/422 to the first request with tools · an empty reply is asked again once · the skip rate is the model's (DeepSeek-V3 1–3 of 12, Qwen2.5-72B 0) and recorded, not prevented · revised 2026-09-26 (#937: a tool call written as text is held back and sent back once, in any turn) | -| 0043 | [Every review queue is governed](0043-every-review-queue-is-governed.md) | Decided 2026-09-14 · no code on `dev` · 0025 gave one queue — duplicate pairs — to an agent that reads the ledger before it decides, and the rest went on waiting for a person who never came: temporal conflicts, low-confidence facts, facts a new document version left stale. Every queue is governed **except nods**, which stay with the person who said the sentence. Each queue keeps its own actions and they are the people's own store calls, so an agent's decision and a person's leave the graph in the same shape; the model decides and the server checks what can be checked (a close date must appear in the evidence, a stale fact is confirmed by quoting the current version), and a failed check turns a confident verdict into a proposal that says why it was held. Every applied action records its undo. Cut 1 was built in PR #699 and that PR was **closed 2026-09-17** to re-land on the open graph after 0044 cut 2; violations, ontology defects and concept mappings are cut 2 | -| 0044 | [The ontology is a view over what documents say](0044-the-ontology-is-a-view-over-what-documents-say.md) | Accepted · cut 1 built (#731, #735, #736, #741, #743–#745): extraction writes open statements, memory documents take the same path, the typed path is deleted, kind words bind to classes · cut 2 (alignment producing typed facts, identity profiles, the errata agent) not built · three layers: extraction writes an open graph in the documents' words (0 of 333 statements unstated in the prototype, against 4–9% of facts bound at write time), a small ontology proposed by an agent and approved by people, and a typed graph computed from the open graph on cached signatures · time mentions resolved against a document time context by code · identity across documents on deterministic evidence before the adjudicator · an errata agent reviews the typed graph | -| 0045 | [A time mention is resolved against its document](0045-a-time-mention-is-resolved-against-its-document.md) | Accepted · cuts 1 and 2 built (#740): a document is dated from its own text, each mention is interpreted by the model and computed by code, upload time is used nowhere · cuts 3 and 4 (grades replace the confidence gate, re-resolution and the anchor queue) not built · a time expression is a mention with its words and place; the model returns shape, anchor, offset and granularity and code computes the interval; a document carries its own date, calendars and anchors across chunks, never its upload time; unresolved mentions wait for an anchor; timelines close on resolution grade instead of confidence | -| 0046 | [The app surface is MCP](0046-the-app-surface-is-mcp.md) | Decided, with the refused design kept. Asked for an app center: applications built on this knowledge, mounted, run in a sandbox, handed to a team. The answer is that the surface already exists — a personal token carries identity and scope, ten read tools serve chat and MCP from one place, `as_of` reaches every graph read, and a read returns `structuredContent` with stable ledger identities — so a coding agent builds on this base today in its own platform, its own language and its own sandbox. Refused here because the layer an app would read is being replaced under it (typed facts now come only from alignment), because 0016 closes open seams before cutting new ones, and because a catalog, an execution boundary and quotas are three other products. The shape is kept with the four gates it would have to hold (runs as the caller, egress only through a declared action, a declared clock, the existing queue) and the dead ends: a container runtime (withdrawn the day it was written — WeKnora's skills are human-written and assume a shell, and they pay for it), a Wasm component runtime (better on every axis including the determinism re-parse needs, still not built because the reason is priority), a service identity per app, an app as a saved conversation. Reopened by a named customer who needs a button inside the product, by the type layer settling, or after 0034 | -| 0047 | [A rule may conclude a relation](0047-a-rule-may-conclude-a-relation.md) | Implemented in #861 (migration 0071) 2026-09-25 · caps unchanged, measured in the PR · A rule reads one entity and concludes about that same entity, so a threshold over a chain — a holding above 50% in a company that itself holds above 50% in another — cannot be written at all, and the query-time path walk that answers it produces no interval, no premises and nothing a queue can see. The conclusion becomes a **relation** between the subject and one entity reached across one declared relation, valid on the intersection of every premise interval including the join edge's. The concluded edge rejoins the pool `derive()` reads and the axiom pass runs once per round, coupling the two reasoners for the first time: 0021's cycle objection is answered with the **finiteness** argument [0030](0030-a-rule-may-read-what-a-rule-concluded.md) already put in place of acyclicity, rather than with a fixed ordering that would let a legitimate rule silently never fire. Reading a value across a hop is [0032](0032-a-rule-computes-what-it-concludes.md)'s decision, reused rather than re-decided. Negation, aggregation, a second hop and user-defined recursion stay out; the existing caps stay in place because this cut changes none of them | -| 0048 | [Provenance references stay inside the knowledge base](0048-provenance-references-stay-inside-the-knowledge-base.md) | Implemented in PR #832 (migration 0070) · a column foreign key proves the target exists, not that it is the same KB's — every reference an export can resolve gets a schema-level same-KB invariant: composite `(kb_id, ref)` foreign keys on the 26 edges whose row carries its own `kb_id` (same-table self-references deferred to commit), row triggers on the 13 whose kb authority is a parent row, `kb_id` immutability on every owned table, and a precondition scan that fails the migration closed on an already-cross-KB ledger · a catalog-derived guard keeps the 39 edges covered in the schema and, with #874, in export preflight · measured populate cost within noise; mechanism choice settled in #832 (discussion in issue #842) | -| 0049 | [Expression declarations are checked when a rule is written](0049-expression-declarations-are-checked-when-a-rule-is-written.md) | Proposed · declaration policy pending; opt-in web draft only | -| 0050 | [An action attempt keeps its identity and uncertain outcome](0050-an-action-attempt-keeps-its-identity-and-uncertain-outcome.md) | Proposed · durable execution identity and uncertain outcomes; no sender | -| 0051 | [A human phrase decision carries its materialization work](0051-a-human-phrase-decision-carries-its-materialization-work.md) | Proposed · decision and materialization delivery; shared refactors and real regressions only | -| 0052 | [Document content is a read contract over the retained ledger](0052-document-content-is-a-read-contract.md) | Proposed 2026-09-21 · implemented in #860 · two Viewer-level reads serve the retained originals the export already names by digest: `/documents/{id}/content[?version=N]` and `/documents/{id}/versions`; the handler locks the document and its ledger row through the blob read, purge answers 410, a ledger-referenced missing blob is a 500 invariant failure, a session or a scoped PAT may read, ingest tokens may not -| 0053 | [A phrase decision records the inputs it considered](0053-a-phrase-decision-records-the-inputs-it-considered.md) | Implemented 2026-09-23 · a decision stores a fingerprint of the ancestor closures and admitted candidates it saw; stale means the fingerprint of the current inputs differs, which is what timestamps could not see (#807, #795): inheritance, parent edges, edits during the request; no-candidate and overflow become recorded outcomes; requeue reads live signatures only, so orphaned rows stop looping · revised 2026-09-26: kind words fingerprinted too, read from one snapshot and compared again when a reply is accepted -| 0054 | [A source may push statements in the open contract](0054-a-source-may-push-statements-in-the-open-contract.md) | Proposed 2026-09-23 · cut 1 in its PR · a `statements` source accepts the open extraction contract (`e`/`s`/`n`) verbatim on `POST /sources/{id}/statements` with the `api` push's identity, versions and tombstones; the payload is stored as one chunk and extraction parses it instead of prompting a model, then runs the unchanged path, so a pushed statement is an open statement and reaches the typed graph only through alignment; there is no slot for a property or class; an update marks earlier statements stale, it does not close them; tables stay on the mount (0036) -| 0060 | [A rule's definition has a history](0060-a-rule-definition-has-a-history.md) | Implemented 2026-09-25 (#912, migration 0076) · A business rule was edited in place and a derivation pointed at the row, so an invalidated conclusion pointed at a rule that now said something else. Every edit that changes what a rule says opens a **version**, a full snapshot with a record time; a derivation names the version it was drawn under, a kept conclusion moves to the new one, and the proof, the rules panel and a versions endpoint read the history. Name, description and the switch open nothing. Exporting rule bodies per version is now honest and stays #902's second cut | -| 0061 | [The ontology is proposed from the open graph and judged by its questions](0061-the-ontology-is-proposed-from-the-open-graph-and-judged-by-its-questions.md) | Accepted · cut 1 built (#947): questions, agent proposals, adoption · cut 1.1 (#950): the agent remembers what it saw, existing answers become map_to · cut 3 first slice (#955): proposed questions, the two numbers, `competency.mjs`; second slice (#962): answers record whether they went through the graph · cut 2, rest of 3, 4 open · A base's unbound signatures and classless kind words are what its documents say and its ontology cannot hold, and nothing says what the ontology is *for*. **Competency questions** become rows of the base and the standard an element is judged by; an **ontology agent** reads the unbound signatures and kind words against them and proposes types, properties and rules with a definition, the statements they would bind and the questions they serve; **approval** writes the element with its structure and re-decides those signatures through 0053's basis, and every approved element carries **regression cases** rerun when its definition changes. Measured by questions answered correctly and by the share of proposals people change. No automatic adoption; the 0003 `Suggest` retires once this replaces it | -| 0062 | [The export says what is contested](0062-the-export-says-what-is-contested.md) | Proposed 2026-09-25 · #564: the RDF export carries `fact_conflicts` and `axiom_violations` as two distinct resource classes — conflicts point at the prior and incoming statements, violations at the statements they are about and (open rows only) the governing criterion and its relation; an ordered `rdf:List` keeps cycle paths; precondition is the current-ontology invariant: `status = 'open'` means reconciled against the current ontology, so criterion-changing ontology edits settle superseded open rows with `criterion_changed` and re-detect in the same transaction -| 0063 | [Stopping a chat ends the generation](0063-stopping-a-chat-ends-the-generation.md) | Implemented 2026-09-27 (#961, migration 0095) · explicit cancellation, saved partial answers with a stopped flag, one active generation per conversation, and one terminal after persistence | - -| | Record | Domain | Status | +By domain; the domains are the files of [../design/](../design/README.md). **Status** is the first word of the record's own status line. **Overtaken** says whether a later record overturned it: `partly (by NNNN)` means some decisions fell and the record's revision notes say which. Open work: `grep 'open:' docs/decisions/0*.md`. + +### [ontology](../design/ontology.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0001 | [Ontology import and governance](0001-ontology-import-and-governance.md) | In progress | partly (by 0009, 0010, 0012, 0044) | +| 0003 | [The ontology grows out of the corpus](0003-ontology-growth-loop.md) | Implemented | partly (by 0007, 0010, 0044) | +| 0007 | [Counting decides what becomes a relation](0007-who-decides-what-becomes-a-relation.md) | Implemented | partly (by 0044) | +| 0008 | [Ontology packs as the cold start](0008-ontology-packs-as-cold-start.md) | Implemented | | +| 0009 | [An undecided type stays empty](0009-no-type-is-a-type.md) | Implemented | | +| 0012 | [The ontology is a contract](0012-the-ontology-is-a-contract-not-a-suggestion.md) | Implemented | partly (by 0044) | +| 0044 | [The ontology is a view over what documents say](0044-the-ontology-is-a-view-over-what-documents-say.md) | In progress | | +| 0051 | [A human phrase decision carries its materialization work](0051-a-human-phrase-decision-carries-its-materialization-work.md) | Implemented | | +| 0053 | [A phrase decision records the inputs it considered](0053-a-phrase-decision-records-the-inputs-it-considered.md) | Implemented | | +| 0061 | [The ontology is proposed from the open graph and judged by its questions](0061-the-ontology-is-proposed-from-the-open-graph-and-judged-by-its-questions.md) | In progress | | +| 0062 | [The export says what is contested](0062-the-export-says-what-is-contested.md) | Implemented | | + +### [extraction](../design/extraction.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0006 | [Ontology scale and the extraction prompt](0006-ontology-scale-and-the-prompt.md) | Superseded | fully (by 0044) | + +### [identity](../design/identity.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0041 | [A name is a claim about an entity](0041-a-name-is-a-claim-about-an-entity.md) | In progress | | + +### [time](../design/time.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0019 | [The second clock can be rewound](0019-the-second-clock-can-be-rewound.md) | Implemented | | +| 0022 | [An unknown date is not an open one](0022-an-unknown-date-is-not-an-open-one.md) | Implemented | partly (by 0045) | +| 0024 | [The world axis reaches the second](0024-the-world-axis-reaches-the-second.md) | Implemented | | +| 0031 | [An event holds at the moment it names](0031-an-event-holds-at-the-moment-it-names.md) | Implemented | | +| 0045 | [A time mention is resolved against its document](0045-a-time-mention-is-resolved-against-its-document.md) | In progress | | + +### [ledger](../design/ledger.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0010 | [An unnamed relation stays empty](0010-no-relation-is-no-relation.md) | Implemented | partly (by 0044) | +| 0037 | [A relation carries its own attributes](0037-a-relation-carries-its-own-attributes.md) | In progress | partly (by 0044) | +| 0048 | [Provenance references stay inside the knowledge base](0048-provenance-references-stay-inside-the-knowledge-base.md) | Implemented | | + +### [governance](../design/governance.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0015 | [A recorded sentence waits for a nod](0015-recording-a-sentence-is-not-asserting-a-fact.md) | Implemented | | +| 0017 | [A contradiction points at an error upstream](0017-a-contradiction-points-upstream.md) | Implemented | | +| 0025 | [Governance reads the ledger before it decides](0025-governance-reads-the-ledger-before-it-decides.md) | Implemented | | +| 0026 | [A decision records why](0026-a-decision-records-why.md) | Implemented | | +| 0027 | [An automatic merge is gated by what it can undo](0027-an-automatic-merge-is-gated-by-what-it-can-undo.md) | Implemented | | +| 0028 | [The adjudicator looks before it asks](0028-the-adjudicator-looks-before-it-asks.md) | Implemented | | +| 0043 | [Every review queue is governed](0043-every-review-queue-is-governed.md) | Accepted | | + +### [rules](../design/rules.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0002 | [Reasoning engine](0002-reasoning-engine.md) | In progress | | +| 0021 | [A rule reads attributes and concludes a type](0021-a-rule-reads-attributes-and-concludes-a-type.md) | Implemented | | +| 0029 | [A rule may say "or", once](0029-a-rule-may-say-or-once.md) | Implemented | | +| 0030 | [A rule may read what a rule concluded](0030-a-rule-may-read-what-a-rule-concluded.md) | In progress | | +| 0032 | [A rule computes what it concludes](0032-a-rule-computes-what-it-concludes.md) | In progress | | +| 0047 | [A rule may conclude a relation](0047-a-rule-may-conclude-a-relation.md) | Implemented | | +| 0049 | [Expression declarations are checked when a rule is written](0049-expression-declarations-are-checked-when-a-rule-is-written.md) | Proposed | | +| 0060 | [A rule's definition has a history](0060-a-rule-definition-has-a-history.md) | Implemented | | + +### [sources](../design/sources.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0013 | [A source hands over its history](0013-a-source-should-hand-over-its-history.md) | Implemented | | +| 0023 | [RSS observations are not documents](0023-rss-observations-are-not-documents.md) | Implemented | | +| 0033 | [RSS summaries are scoped to the source being listed](0033-rss-source-summaries-are-source-scoped.md) | Implemented | | +| 0035 | [A vector index is built by a job](0035-a-vector-index-is-built-by-a-job.md) | Implemented | | +| 0039 | [A chunk is what extraction sees](0039-a-chunk-is-what-extraction-sees.md) | In progress | | +| 0040 | [A chunk says where its words came from](0040-a-chunk-says-where-its-words-came-from.md) | Implemented | | +| 0052 | [Document content is a read contract over the retained ledger](0052-document-content-is-a-read-contract.md) | Proposed | | +| 0054 | [A source may push statements in the open contract](0054-a-source-may-push-statements-in-the-open-contract.md) | Implemented | | + +### [lakehouse-and-actions](../design/lakehouse-and-actions.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0011 | [A mapping is configuration](0011-a-mapping-is-not-a-fact.md) | Implemented | partly (by 0036, 0044) | +| 0018 | [The lakehouse is one protocol away](0018-the-lakehouse-is-one-protocol-away.md) | Implemented | | +| 0034 | [An action is a declared call](0034-an-action-is-a-declared-call.md) | Proposed | | +| 0036 | [Exploration aligns a schema to the ontology](0036-exploration-aligns-a-schema-to-the-ontology.md) | In progress | | +| 0050 | [An action attempt keeps its identity and uncertain outcome](0050-an-action-attempt-keeps-its-identity-and-uncertain-outcome.md) | Proposed | | + +### [access-and-audit](../design/access-and-audit.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0014 | [Identity from the person, scope from the token](0014-identity-from-the-person-scope-from-the-token.md) | Implemented | | +| 0020 | [An auditor reads it without us](0020-an-auditor-reads-it-without-us.md) | Implemented | | + +### [interface](../design/interface.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0004 | [Language follows the reader of each text](0004-language-and-localization.md) | Implemented | partly (by 0044) | +| 0005 | [The alert center](0005-alert-center.md) | Implemented | | +| 0038 | [The interface has a light side](0038-the-interface-has-a-light-side.md) | Implemented | | + +### [chat-and-mcp](../design/chat-and-mcp.md) + +| | Record | Status | Overtaken | +|---|---|---|---| +| 0042 | [The chat loop is a runner with hooks](0042-the-chat-loop-is-a-runner-with-hooks.md) | Implemented | | +| 0046 | [The app surface is MCP](0046-the-app-surface-is-mcp.md) | Accepted | | +| 0063 | [Stopping a chat ends the generation](0063-stopping-a-chat-ends-the-generation.md) | Implemented | | + +### process + +| | Record | Status | Overtaken | |---|---|---|---| -| 0001 | [Ontology import and governance](0001-ontology-import-and-governance.md) | ontology | partly superseded (by 0009, 0010, 0012, 0044) | -| 0002 | [Reasoning engine](0002-reasoning-engine.md) | rules | current | -| 0003 | [The ontology grows out of the corpus](0003-ontology-growth-loop.md) | ontology | partly superseded (by 0007, 0010, 0044) | -| 0004 | [Language follows the reader of each text](0004-language-and-localization.md) | interface | partly superseded (by 0044) | -| 0005 | [The alert center](0005-alert-center.md) | interface | current | -| 0006 | [Ontology scale and the extraction prompt](0006-ontology-scale-and-the-prompt.md) | extraction | superseded (by 0044) | -| 0007 | [Counting decides what becomes a relation](0007-who-decides-what-becomes-a-relation.md) | ontology | partly superseded (by 0044) | -| 0008 | [Ontology packs as the cold start](0008-ontology-packs-as-cold-start.md) | ontology | current | -| 0009 | [An undecided type stays empty](0009-no-type-is-a-type.md) | ontology | current | -| 0010 | [An unnamed relation stays empty](0010-no-relation-is-no-relation.md) | ledger | partly superseded (by 0044) | -| 0011 | [A mapping is configuration](0011-a-mapping-is-not-a-fact.md) | lakehouse-and-actions | partly superseded (by 0036, 0044) | -| 0012 | [The ontology is a contract](0012-the-ontology-is-a-contract-not-a-suggestion.md) | ontology | partly superseded (by 0044) | -| 0013 | [A source hands over its history](0013-a-source-should-hand-over-its-history.md) | sources | current | -| 0014 | [Identity from the person, scope from the token](0014-identity-from-the-person-scope-from-the-token.md) | access-and-audit | current | -| 0015 | [A recorded sentence waits for a nod](0015-recording-a-sentence-is-not-asserting-a-fact.md) | governance | current | -| 0016 | [Close the open seams before cutting new ones](0016-close-the-open-seams-before-cutting-new-ones.md) | process | partly superseded (by 0036) | -| 0017 | [A contradiction points at an error upstream](0017-a-contradiction-points-upstream.md) | governance | current | -| 0018 | [The lakehouse is one protocol away](0018-the-lakehouse-is-one-protocol-away.md) | lakehouse-and-actions | current | -| 0019 | [The second clock can be rewound](0019-the-second-clock-can-be-rewound.md) | time | current | -| 0020 | [An auditor reads it without us](0020-an-auditor-reads-it-without-us.md) | access-and-audit | current | -| 0021 | [A rule reads attributes and concludes a type](0021-a-rule-reads-attributes-and-concludes-a-type.md) | rules | current | -| 0022 | [An unknown date is not an open one](0022-an-unknown-date-is-not-an-open-one.md) | time | partly superseded (by 0045) | -| 0023 | [RSS observations are not documents](0023-rss-observations-are-not-documents.md) | sources | current | -| 0024 | [The world axis reaches the second](0024-the-world-axis-reaches-the-second.md) | time | current | -| 0025 | [Governance reads the ledger before it decides](0025-governance-reads-the-ledger-before-it-decides.md) | governance | current | -| 0026 | [A decision records why](0026-a-decision-records-why.md) | governance | current | -| 0027 | [An automatic merge is gated by what it can undo](0027-an-automatic-merge-is-gated-by-what-it-can-undo.md) | governance | current | -| 0028 | [The adjudicator looks before it asks](0028-the-adjudicator-looks-before-it-asks.md) | governance | current | -| 0029 | [A rule may say "or", once](0029-a-rule-may-say-or-once.md) | rules | current | -| 0030 | [A rule may read what a rule concluded](0030-a-rule-may-read-what-a-rule-concluded.md) | rules | current | -| 0031 | [An event holds at the moment it names](0031-an-event-holds-at-the-moment-it-names.md) | time | current | -| 0032 | [A rule computes what it concludes](0032-a-rule-computes-what-it-concludes.md) | rules | current | -| 0033 | [RSS summaries are scoped to the source being listed](0033-rss-source-summaries-are-source-scoped.md) | sources | current | -| 0034 | [An action is a declared call](0034-an-action-is-a-declared-call.md) | lakehouse-and-actions | proposed | -| 0035 | [A vector index is built by a job](0035-a-vector-index-is-built-by-a-job.md) | sources | current | -| 0036 | [Exploration aligns a schema to the ontology](0036-exploration-aligns-a-schema-to-the-ontology.md) | lakehouse-and-actions | current | -| 0037 | [A relation carries its own attributes](0037-a-relation-carries-its-own-attributes.md) | ledger | partly superseded (by 0044) | -| 0038 | [The interface has a light side](0038-the-interface-has-a-light-side.md) | interface | current | -| 0039 | [A chunk is what extraction sees](0039-a-chunk-is-what-extraction-sees.md) | sources | current | -| 0040 | [A chunk says where its words came from](0040-a-chunk-says-where-its-words-came-from.md) | sources | current | -| 0041 | [A name is a claim about an entity](0041-a-name-is-a-claim-about-an-entity.md) | identity | current | -| 0042 | [The chat loop is a runner with hooks](0042-the-chat-loop-is-a-runner-with-hooks.md) | chat-and-mcp | current | -| 0043 | [Every review queue is governed](0043-every-review-queue-is-governed.md) | governance | current | -| 0044 | [The ontology is a view over what documents say](0044-the-ontology-is-a-view-over-what-documents-say.md) | ontology | current | -| 0045 | [A time mention is resolved against its document](0045-a-time-mention-is-resolved-against-its-document.md) | time | current | -| 0046 | [The app surface is MCP](0046-the-app-surface-is-mcp.md) | chat-and-mcp | current | -| 0047 | [A rule may conclude a relation](0047-a-rule-may-conclude-a-relation.md) | rules | current | -| 0048 | [Provenance references stay inside the knowledge base](0048-provenance-references-stay-inside-the-knowledge-base.md) | ledger | current | -| 0049 | [Expression declarations are checked when a rule is written](0049-expression-declarations-are-checked-when-a-rule-is-written.md) | rules | proposed | -| 0050 | [An action attempt keeps its identity and uncertain outcome](0050-an-action-attempt-keeps-its-identity-and-uncertain-outcome.md) | lakehouse-and-actions | proposed | -| 0051 | [A human phrase decision carries its materialization work](0051-a-human-phrase-decision-carries-its-materialization-work.md) | ontology | proposed | -| 0052 | [Document content is a read contract over the retained ledger](0052-document-content-is-a-read-contract.md) | sources | current | -| 0053 | [A phrase decision records the inputs it considered](0053-a-phrase-decision-records-the-inputs-it-considered.md) | ontology | current | -| 0054 | [A source may push statements in the open contract](0054-a-source-may-push-statements-in-the-open-contract.md) | sources | proposed | -| 0060 | [A rule's definition has a history](0060-a-rule-definition-has-a-history.md) | rules | current | -| 0061 | [The ontology is proposed from the open graph and judged by its questions](0061-the-ontology-is-proposed-from-the-open-graph-and-judged-by-its-questions.md) | ontology | current | -| 0062 | [The export says what is contested](0062-the-export-says-what-is-contested.md) | ontology | proposed | -| 0063 | [Stopping a chat ends the generation](0063-stopping-a-chat-ends-the-generation.md) | chat-and-mcp | current | - -The status word is whether a later record has overtaken this one; what is built is in the record's own status line. Domains are the files of [../design/](../design/README.md), where every record is dated and the status words are defined. +| 0016 | [Close the open seams before cutting new ones](0016-close-the-open-seams-before-cutting-new-ones.md) | In progress | partly (by 0036) | ## Not a decision record diff --git a/docs/design/README.md b/docs/design/README.md index 599f385e7..cd22999bf 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -42,8 +42,8 @@ model reads, the growth loop fed from the model's predicate words, qualifiers ke attributes at extraction) and, for the same reason, the write-time direction correction of 0012 and the input of 0007's counting loop. Their ledger decisions stand: a predicate may be null and display falls back to the source's wording (0010), evidence on every fact and every drop a row (0001), -qualifiers live beside the edge (0037), an adoption rewrite is an append with undo (0003). The table -below marks these records from this note; their own status lines predate it and were not touched. +qualifiers live beside the edge (0037), an adoption rewrite is an append with undo (0003). The index +marks these records from this note; their own status lines predate it and were not touched. ## Status words @@ -52,58 +52,12 @@ the note above, overturned some of its decisions and the rest hold; the record's say which. `superseded (by NNNN)`: none of its decisions hold. `proposed`: the direction is accepted and the code is not yet on `dev` (0034 has no code; 0043's cut 1 was in PR #699, closed on 2026-09-17 to re-land on the open graph after alignment, while the record itself is on `dev`; 0044 and 0045 sit in PRs #710 and #724, and 0044's cut 1 has landed ahead of the record). The record's own status line -is the source of truth for what is built; this table only adds whether a later record has overtaken +is the source of truth for what is built; the index only adds whether a later record has overtaken it. ## Every record -| | Date | Record | Domain | Status | -|---|---|---|---|---| -| 0001 | 2026-08-27 | [Ontology import and governance](../decisions/0001-ontology-import-and-governance.md) | ontology | partly superseded (by 0009, 0010, 0012, 0044) | -| 0002 | 2026-08-28 | [Reasoning engine](../decisions/0002-reasoning-engine.md) | rules | current | -| 0003 | 2026-08-28 | [The ontology grows out of the corpus](../decisions/0003-ontology-growth-loop.md) | ontology | partly superseded (by 0007, 0010, 0044) | -| 0004 | 2026-08-29 | [Language follows the reader of each text](../decisions/0004-language-and-localization.md) | interface | partly superseded (by 0044) | -| 0005 | 2026-08-29 | [The alert center](../decisions/0005-alert-center.md) | interface | current | -| 0006 | 2026-08-29 | [Ontology scale and the extraction prompt](../decisions/0006-ontology-scale-and-the-prompt.md) | extraction | superseded (by 0044) | -| 0007 | 2026-08-30 | [Counting decides what becomes a relation](../decisions/0007-who-decides-what-becomes-a-relation.md) | ontology | partly superseded (by 0044) | -| 0008 | 2026-08-30 | [Ontology packs as the cold start](../decisions/0008-ontology-packs-as-cold-start.md) | ontology | current | -| 0009 | 2026-08-30 | [An undecided type stays empty](../decisions/0009-no-type-is-a-type.md) | ontology | current | -| 0010 | 2026-08-30 | [An unnamed relation stays empty](../decisions/0010-no-relation-is-no-relation.md) | ledger | partly superseded (by 0044) | -| 0011 | 2026-08-31 | [A mapping is configuration](../decisions/0011-a-mapping-is-not-a-fact.md) | lakehouse-and-actions | partly superseded (by 0036, 0044) | -| 0012 | 2026-08-31 | [The ontology is a contract](../decisions/0012-the-ontology-is-a-contract-not-a-suggestion.md) | ontology | partly superseded (by 0044) | -| 0013 | 2026-08-31 | [A source hands over its history](../decisions/0013-a-source-should-hand-over-its-history.md) | sources | current | -| 0014 | 2026-09-01 | [Identity from the person, scope from the token](../decisions/0014-identity-from-the-person-scope-from-the-token.md) | access-and-audit | current | -| 0015 | 2026-09-01 | [A recorded sentence waits for a nod](../decisions/0015-recording-a-sentence-is-not-asserting-a-fact.md) | governance | current | -| 0016 | 2026-09-02 | [Close the open seams before cutting new ones](../decisions/0016-close-the-open-seams-before-cutting-new-ones.md) | process | partly superseded (by 0036) | -| 0017 | 2026-09-03 | [A contradiction points at an error upstream](../decisions/0017-a-contradiction-points-upstream.md) | governance | current | -| 0018 | 2026-09-03 | [The lakehouse is one protocol away](../decisions/0018-the-lakehouse-is-one-protocol-away.md) | lakehouse-and-actions | current | -| 0019 | 2026-09-04 | [The second clock can be rewound](../decisions/0019-the-second-clock-can-be-rewound.md) | time | current | -| 0020 | 2026-09-05 | [An auditor reads it without us](../decisions/0020-an-auditor-reads-it-without-us.md) | access-and-audit | current | -| 0021 | 2026-09-05 | [A rule reads attributes and concludes a type](../decisions/0021-a-rule-reads-attributes-and-concludes-a-type.md) | rules | current | -| 0022 | 2026-09-06 | [An unknown date is not an open one](../decisions/0022-an-unknown-date-is-not-an-open-one.md) | time | partly superseded (by 0045) | -| 0023 | 2026-09-05 | [RSS observations are not documents](../decisions/0023-rss-observations-are-not-documents.md) | sources | current | -| 0024 | 2026-09-06 | [The world axis reaches the second](../decisions/0024-the-world-axis-reaches-the-second.md) | time | current | -| 0025 | 2026-09-06 | [Governance reads the ledger before it decides](../decisions/0025-governance-reads-the-ledger-before-it-decides.md) | governance | current | -| 0026 | 2026-09-08 | [A decision records why](../decisions/0026-a-decision-records-why.md) | governance | current | -| 0027 | 2026-09-08 | [An automatic merge is gated by what it can undo](../decisions/0027-an-automatic-merge-is-gated-by-what-it-can-undo.md) | governance | current | -| 0028 | 2026-09-08 | [The adjudicator looks before it asks](../decisions/0028-the-adjudicator-looks-before-it-asks.md) | governance | current | -| 0029 | 2026-09-08 | [A rule may say "or", once](../decisions/0029-a-rule-may-say-or-once.md) | rules | current | -| 0030 | 2026-09-08 | [A rule may read what a rule concluded](../decisions/0030-a-rule-may-read-what-a-rule-concluded.md) | rules | current | -| 0031 | 2026-09-08 | [An event holds at the moment it names](../decisions/0031-an-event-holds-at-the-moment-it-names.md) | time | current | -| 0032 | 2026-09-08 | [A rule computes what it concludes](../decisions/0032-a-rule-computes-what-it-concludes.md) | rules | current | -| 0033 | 2026-09-06 | [RSS summaries are scoped to the source being listed](../decisions/0033-rss-source-summaries-are-source-scoped.md) | sources | current | -| 0034 | 2026-09-08 | [An action is a declared call](../decisions/0034-an-action-is-a-declared-call.md) | lakehouse-and-actions | proposed | -| 0035 | 2026-09-09 | [A vector index is built by a job](../decisions/0035-a-vector-index-is-built-by-a-job.md) | sources | current | -| 0036 | 2026-09-09 | [Exploration aligns a schema to the ontology](../decisions/0036-exploration-aligns-a-schema-to-the-ontology.md) | lakehouse-and-actions | current | -| 0037 | 2026-09-10 | [A relation carries its own attributes](../decisions/0037-a-relation-carries-its-own-attributes.md) | ledger | partly superseded (by 0044) | -| 0038 | 2026-09-11 | [The interface has a light side](../decisions/0038-the-interface-has-a-light-side.md) | interface | current | -| 0039 | 2026-09-13 | [A chunk is what extraction sees](../decisions/0039-a-chunk-is-what-extraction-sees.md) | sources | current | -| 0040 | 2026-09-13 | [A chunk says where its words came from](../decisions/0040-a-chunk-says-where-its-words-came-from.md) | sources | current | -| 0041 | 2026-09-13 | [A name is a claim about an entity](../decisions/0041-a-name-is-a-claim-about-an-entity.md) | identity | current | -| 0042 | 2026-09-13 | [The chat loop is a runner with hooks](../decisions/0042-the-chat-loop-is-a-runner-with-hooks.md) | chat-and-mcp | current | -| 0043 | 2026-09-14 | Every review queue is governed (was PR #699, closed 2026-09-17; re-lands after 0044 cut 2) | governance | proposed | -| 0044 | 2026-09-16 | [The ontology is a view over what documents say](../decisions/0044-the-ontology-is-a-view-over-what-documents-say.md) | ontology | proposed | -| 0045 | 2026-09-16 | [A time mention is resolved against its document](../decisions/0045-a-time-mention-is-resolved-against-its-document.md) | time | proposed | +The records are listed by domain in the [index](../decisions/README.md#index), with these words in its last column. [prior-work.md](prior-work.md) is not a domain: it places each layer in the literature it stands on and lists the pitfalls taken from it, with what is still open.