@antoneeo/kb-agentic-skill 1.31.0 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +2 -2
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/skills/kb-agentic-skill/ENFORCEMENT.md +3 -3
- package/skills/kb-agentic-skill/SKILL.md +17 -3
- package/skills/kb-agentic-skill/distillation.md +15 -5
- package/skills/kb-agentic-skill/docs_site/app.js +7 -1
- package/skills/kb-agentic-skill/docs_site/strings.js +9 -0
- package/skills/kb-agentic-skill/docs_site/style.css +2 -0
- package/skills/kb-agentic-skill/guides.md +8 -0
- package/skills/kb-agentic-skill/portability.md +52 -16
- package/skills/kb-agentic-skill/reconciliation.md +29 -8
- package/skills/kb-agentic-skill/review.md +9 -1
- package/skills/kb-agentic-skill/scripts/docs_site.py +17 -3
- package/skills/kb-agentic-skill/scripts/knowledge.py +925 -33
- package/skills/kb-agentic-skill/scripts/sdlc_core.py +562 -22
- package/skills/kb-agentic-skill/templates.md +46 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
Every significant change to this skill is recorded here.
|
|
4
4
|
|
|
5
|
+
## [1.32.0] - 2026-10-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `inventory <path>`: every project file under a path (`.` for all), each with its state against the corpus, compared by content — `INGESTED` (its bytes are a sidecar's digest, wherever it lives, or its `original_path:` with no digest kept), `INGESTED_EARLIER` (the original changed since ingest), `EXCLUDED` (a row of the approved registry `audit/INVENTORY_exclusions.md`; never read), `NOT_EXAMINED` (the default; links, junctions and cloud-only files are never read), and once per built-in skipped directory `SKIPPED`. No totals, no filter by state; it writes nothing (`vision/rulings.md` r37).
|
|
9
|
+
- Orient prints, in a project with a corpus, an upper bound of the project files never examined (`Corpus reach: <= N …`); Topic Recall runs `inventory` on the plausible path before answering `kb: no coverage` and says what it found there. `stale` names an ingested artifact whose local original changed since ingest, until a successor supersedes it.
|
|
10
|
+
- Documents the project sends out: the version sent enters `corpus/given/` with `provenance: DECLARED`, `recipient:` and the send date; its claims are `DECLARED` ("declared to X on D"), never evidence about the product. A declaration that contradicts the ledger is held as `DIVERGES <id>` on the declaration only; the ledger row is untouched. `export` leaves declarations behind and says so; `import` refuses them (r38).
|
|
11
|
+
- `check` (every lens) now refuses a non-DECLARED row citing a sent document in any source position, a DECLARED row without `recipient:`, `SUPERSEDED`/`CONTESTED` across a declaration, a malformed exclusion-registry row, and a `DIVERGES` on a non-DECLARED row or at a missing, DECLARED or superseded target. A GIVEN row on a `provenance: DECLARED` sidecar used to warn; it is now an error. The docs site shows the `declared` origin, the `diverges` state, ground resting on a sent document, and "document sent" in the chronology.
|
|
12
|
+
- `constraints --unit <id>` and `constraints --file <path>` (shared core): the constraints an ANALYSIS states in extractable form, each with its line, ending with the sections they could not extract.
|
|
13
|
+
- `benefit` and the docs site recognize a REVIEW_LOG row with tier `post-merge` (written by the code lens: the owner's report of a wrong behaviour in a merged change): `benefit` keeps it out of every review figure, the docs site shows it as "defect reported after merge". `review.md`: a reviewer derives callers and behaviours before comparing with the constraint view.
|
|
14
|
+
- `guides.md`: a code guide may carry a `## Preservation constraints` section copied from a closed unit; it survives regeneration. Nothing else changes in this lens: `validate` adds no note.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `import` now does what `portability.md` §4 promised: a ruling from another project arrives as `prov: IMPORTED` and settles nothing until you ratify it. It used to arrive unchanged as `RULING`, so another project's decision was in force here with `check` clean. Its note arrives as a renamed copy, `corpus/notes/imported_<project>_<name>`, carrying `imported_from:` and `origin: imported`; the note is never edited under its own name, because an inserted line would move the line every locator cites. The rows the ruling superseded and the ruling itself arrive as one `CONTESTED` set. Ratifying is an ordinary ruling: your own note with your own `basis:`, a `RULING` row on it, and the contested rows `SUPERSEDED` by it. `--dry-run` reports the conversions too.
|
|
18
|
+
- `import` refuses, writing nothing and naming the row, a bundle whose conflict sets would arrive incomplete (a pointer resolving to no row, or to a row of this project that does not name it back), a row settled by a ruling the import does not write or by a row held as `IMPORTED`, and a ruling it cannot carry foreign (not on a note cited by line; on a note with no frontmatter or one already naming a foreign origin; superseded both by a ruling and by another row). `portability.md` §3 already listed an incomplete conflict set as a refusal; it is now enforced. §3 no longer lists a dangling `supersedes:` as a refusal: the import never checked it, and `check` reports it after the import.
|
|
19
|
+
- Any bundle note that declares a `basis:` lands as the renamed foreign copy, even when no ruling row travels with it, so a later import of its ruling cannot find it under its own name. A note in UTF-8 with a BOM (Windows editors) converts and keeps its BOM.
|
|
20
|
+
- `check` refuses a `RULING` row on a note that names a foreign origin (`imported_from:`): relabelling an imported row is not ratifying it.
|
|
21
|
+
- A project imported with kb 1.31.0 or earlier holds a foreign ruling as `RULING`: delete the imported topic and its files and import again, or convert it by hand (`portability.md` §4).
|
|
22
|
+
- Guide-verdict check (`verdict --hook`, shared core): a level quoted inside a sentence (`Level: L1` in prose) no longer counts as a declaration: only a line that opens with the level word does, the rule the constraint recall already uses (level 1 alone, `L1` or `E1`; levels 2 and 3 with `router` on the same line). The check now judges the project, and its docs-root name, that holds the edited file, not the one around the session's working directory; after a `cd` out of the project it let every edit through. A router verdict follows the same rule: only a line that opens with `router` or declares the level with it counts, never `router: no match` quoted in a sentence; `Level: Spike · router: …` and markdown openers (`##`, `1.`, `•`) count. A quote that itself opens a line (blockquote, code fence) still counts. The protected-path gate (`gate --hook`, `gate --file`) also judges the edited file's project, falls back to the session's project when no docs root sits above the file, and stays silent instead of exiting with an error when two docs roots sit side by side. Its `--protected` prefixes are relative to the nearest docs root above the edited file: with nested docs roots (a monorepo package with its own `ai_docs/`, or a docs folder inside a protected path) rewrite them relative to that root, or the path is no longer protected.
|
|
23
|
+
|
|
5
24
|
## [1.31.0] - 2026-10-23
|
|
6
25
|
|
|
7
26
|
### Changed
|
package/README.md
CHANGED
|
@@ -47,14 +47,14 @@ Deliberately absent: any per-node coverage or completion state. `gaps:` says wha
|
|
|
47
47
|
## Key features
|
|
48
48
|
|
|
49
49
|
- **Risk-proportional triage, measured in knowledge and never in files**: one claim row → propagating a fact already settled → a new knowledge unit (a source ingested, a node created or superseded, the hierarchy moved). Carrying one settled fact into eight documents is small; one claim that re-parents a node is not. On a live front, a new source or statement within an open unit's declared scope is an **ingestion step** of that unit — recorded on the ledger at once — while the rewrite of dependent documents, the registry and the full check run once, at a closure the practitioner calls. A **Write Triggers** table maps each knowledge event to exactly one destination.
|
|
50
|
-
- **Portable knowledge**: `export` bundles a subgraph together with the bytes its claims cite — a closure, not a selection, because a claim whose source cannot be reopened is model knowledge arriving by another route. `import` merges it into another project **additively**: it never overwrites a node and never deletes, and claims already present are recognised by id rather than by comparing text, since the same artifact cited at the same span mints the same id in every project. Knowledge crosses the project boundary; **authority does not** — an imported ruling arrives as `prov: IMPORTED
|
|
50
|
+
- **Portable knowledge**: `export` bundles a subgraph together with the bytes its claims cite — a closure, not a selection, because a claim whose source cannot be reopened is model knowledge arriving by another route. `import` merges it into another project **additively**: it never overwrites a node and never deletes, and claims already present are recognised by id rather than by comparing text, since the same artifact cited at the same span mints the same id in every project. Knowledge crosses the project boundary; **authority does not** — an imported ruling arrives as `prov: IMPORTED` on a renamed copy of its note that names its origin, keeps its original `basis:`, and the question it had closed is open again (`CONTESTED`) until you ratify it with a ruling of your own.
|
|
51
51
|
- **Several people, one project**: the workstream registry (`audit/handoff.md`) is **generated** from one file per open workstream, so two people opening or closing two workstreams on two branches edit two different files and their merge is clean. Row-per-workstream alone was not enough — a file-global `Date:` header defeats row-level ownership — so the header is derived from the sources and no writer touches it. The generated view can still conflict; that conflict is resolved by re-running `index`, never by hand, and `validate` refuses CLEAN until the file matches its sources. The append-only review log gets `merge=union` (a built-in driver, no per-clone configuration). It all works with no VCS at all: it is files and a generator.
|
|
52
52
|
- **Vision-guided governance**: Standalone projects use `ai_docs/vision/`; Hybrid projects use devPNT `M-VISION` as the milestone north star. `DRAFT` informs, `APPROVED` binds, promotion is the user's alone. Every Vision, the M-VISION included, is written only after a six-question **owner interview** and your explicit approval of a one- or two-sentence north star in your own words — the agent never writes one alone.
|
|
53
53
|
- **Independent review, twice**: the design before it is implemented, the result before it is declared done — fresh-context subagent > one-shot run > a declared self-pass, 3 rounds max, one log line each, and a PASS is invalid on "found nothing".
|
|
54
54
|
- **Question discipline**: a real doubt is asked when it emerges, before its answer is written, with the pros and cons of each option; a choice whose pros clearly win is taken with the weighing written next to it. A question is legal only when the agent searched first, names the search with its result, and names the decision it unblocks.
|
|
55
55
|
- **Verbatim request record**: every L3 starts `audit/requests/REQUEST_[topic].md` before the Vision Gate — the practitioner's own words and every later decision (answers, approvals, costs, rulings), append-only and redacted for secrets — so analyses cite what was asked instead of paraphrasing it, and reviews check owner statements against it.
|
|
56
56
|
- **Operative guides + agent-global KB**: distil user-provided indications into source-faithful `GUIDE_*.md` (`source_kind: document`) — verbatim snapshot plus hash, so drift is detected mechanically.
|
|
57
|
-
- **Mechanical checks**: `check`, `validate`, `index`, `graph`, `corpus`, `claim-id`, `anchor`, `export`, `import`, `adopt` (dry run: document guides on snapshots and what moving them into the corpus involves), `stale` (spine staleness plus `## claims` -- superseded-cited documents, expired `valid` windows, superseded derivation chains), plus the spine's `mark`/`benefit`/`gate`/`plan`/`orient`/`migrate`. `benefit` reports what the review gates have already caught, beside what the doctrine costs to read; it always exits 0 and carries no verdict. The graph and corpus checks verify spans against the stored extraction, recompute every id, refuse cycles and unreachable nodes, and enforce `CONTESTED` symmetry.
|
|
57
|
+
- **Mechanical checks**: `check`, `validate`, `index`, `graph`, `corpus`, `claim-id`, `anchor`, `export`, `import`, `adopt` (dry run: document guides on snapshots and what moving them into the corpus involves), `inventory <path>` (each project file under a path against the corpus: `INGESTED`, `INGESTED_EARLIER`, `EXCLUDED`, `NOT_EXAMINED`, `SKIPPED`; orient prints an upper bound of the files never examined), `stale` (spine staleness plus `## claims` -- superseded-cited documents, expired `valid` windows, superseded derivation chains), plus the spine's `mark`/`benefit`/`gate`/`plan`/`orient`/`migrate`. `benefit` reports what the review gates have already caught, beside what the doctrine costs to read; it always exits 0 and carries no verdict. The graph and corpus checks verify spans against the stored extraction, recompute every id, refuse cycles and unreachable nodes, and enforce `CONTESTED` symmetry.
|
|
58
58
|
- **Installed support files**: Claude, Codex, Gemini and Google Antigravity receive the full skill folder — `SKILL.md`, `templates.md`, `taxonomy.md`, `distillation.md`, `reconciliation.md`, `guides.md`, `vision.md`, `elicitation.md`, `review.md`, `dispatch.md`, `routing.md`, `ENFORCEMENT.md`, `memory.md`, and the validator's three files, `scripts/sdlc_check.py` + `scripts/sdlc_core.py` + `scripts/knowledge.py` (copy all three) — plus the docs site generator `scripts/docs_site.py` and its page `docs_site/`, and the `LICENSE` and `NOTICE` it ships under.
|
|
59
59
|
- **Docs site** (`scripts/docs_site.py --root <project> [--open] [--zip <file>]`): a static, offline, read-only website of what the agent reads in the project — reading guide, Vision, rules, documents, open work with each next step, analyses and, with a knowledge base, the topic map, each fact with its exact source passage, and the source documents. Written to `.docs-site/` (added to `.gitignore` on the first run), outside `ai_docs/`; nothing is ranked or recommended.
|
|
60
60
|
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kb-agentic-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.32.0",
|
|
4
4
|
"description": "Knowledge-Base & Document-First protocol with risk triage, Vision governance, signal distillation and optional devPNT integration.",
|
|
5
5
|
"author": "Antonio Pinto (https://github.com/Antoneeo)"
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antoneeo/kb-agentic-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.32.0",
|
|
4
4
|
"description": "Knowledge-Base & Document-First protocol for Claude Code, Gemini CLI, Google Antigravity and Codex with risk triage, Vision governance, signal distillation and optional devPNT integration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -99,15 +99,15 @@ Semantics: exit code 2 + message on stderr ⇒ the write is blocked and the mess
|
|
|
99
99
|
|
|
100
100
|
**Usage warnings:**
|
|
101
101
|
- The gate is deliberately coarse: applied to all of `src/` it would also block the legitimate L1/L2 tasks foreseen by the Triage. Use it **only on security-critical directories** (`--protected "src/auth;src/crypto"`), where "never without analysis" is the desired policy.
|
|
102
|
-
- The paths in `--protected` are prefixes relative to the project
|
|
102
|
+
- The paths in `--protected` are prefixes relative to the root of the project the hook judges (below), separated by `;`. With nested docs roots (a monorepo whose package has its own), that is the nearest one above the edited file: write the prefixes relative to it.
|
|
103
103
|
- `ai_docs/`, `tests/` and `test/` are always excluded from blocking.
|
|
104
|
-
- The hook
|
|
104
|
+
- The hook judges the project, and the docs root, holding the edited file: the session's working directory can move with a `cd`, so it is never the reference. With no docs root above the file, it is the session's own project, as before. `--root <path>` overrides it; `gate --file` resolves the same way, and two docs roots side by side leave the gate silent.
|
|
105
105
|
- **Hybrid/devPNT projects**: add `--hybrid` to the gate command. Governed designs live in the devPNT DB, so the gate also unlocks when an approved E-TDD shadow (`ai_docs/solutions/SHADOW_*tdd*.md`, exported before implementation — in this package that clause is the whole statement of the duty; the sibling lenses keep it in their `hybrid.md`, which kb does not have) is present. Without the flag the gate would block legitimate governed work. The flag is deliberately explicit: never auto-detected.
|
|
106
106
|
|
|
107
107
|
### Verdict check: `verdict` (PreToolUse -- manual for this lens)
|
|
108
108
|
|
|
109
109
|
`sdlc_check.py verdict --hook` refuses an Edit, Write, MultiEdit or NotebookEdit when the
|
|
110
|
-
session transcript shows no guide-router verdict (`router: …`, or a level-1 declaration)
|
|
110
|
+
session transcript shows no guide-router verdict (a line that opens with `router: …` or with the level, `Level: L2 · router: …`, `Level: Spike · router: …`; or one that opens with a level-1 declaration)
|
|
111
111
|
or a guide the verdict names was never read; it is silent in projects without guides and
|
|
112
112
|
whenever it cannot tell. The code package (`agentic-sdlc`) wires it machine-wide on
|
|
113
113
|
Claude Code; this package does not. To wire it by hand, add a `PreToolUse` entry with
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kb-agentic
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.32.0
|
|
4
4
|
description: Knowledge-Base & Document-First protocol with risk-proportional triage, Vision as a guide, Signal Distillation, a complete Standalone mode and optional symbiosis with devPNT. Use for user documentation, knowledge extraction, SOPs, research notes, decision logs, knowledge management — and for answering project questions from the claim ledger and topic graph.
|
|
5
5
|
author: Antonio Pinto (https://github.com/Antoneeo)
|
|
6
6
|
copyright: (c) 2026 Antonio Pinto
|
|
@@ -24,7 +24,7 @@ Support files in the skill directory:
|
|
|
24
24
|
- `review.md`: the review discipline — when a review is due, how to request one, how to receive findings, how to review.
|
|
25
25
|
- `dispatch.md`: opt-in subagent execution of an approved plan, plus the family-wide delegation boundary (what may be delegated at all) — that part applies at any level.
|
|
26
26
|
- `routing.md`: which lens owns this unit of work. Read ONLY when a sibling lens skill is installed alongside this one; a single-lens install never reads it.
|
|
27
|
-
- `scripts/sdlc_check.py` + `scripts/sdlc_core.py`: the mechanical validator for the docs root (`check`, `validate`, `index`, `mark`, `benefit`, `gate`, `plan`, `orient`, `migrate`, and the knowledge overlay: `graph`, `corpus`, `claim-id`, `anchor`, `export`, `import`, `adopt` (dry run of an adoption), `stale` (spine staleness plus `## claims` -- the time cycle's consolidated view), plus `remind` — the opt-in per-turn one-line reminder, ENFORCEMENT §4). Keep the entry point, `scripts/sdlc_core.py` and `scripts/knowledge.py` together; the last supplies common memory and KB integrity.
|
|
27
|
+
- `scripts/sdlc_check.py` + `scripts/sdlc_core.py`: the mechanical validator for the docs root (`check`, `validate`, `index`, `mark`, `benefit`, `gate`, `plan`, `orient`, `migrate`, and the knowledge overlay: `graph`, `corpus`, `claim-id`, `anchor`, `export`, `import`, `adopt` (dry run of an adoption), `inventory <path>` (the project files under a path, each with its state against the corpus), `stale` (spine staleness plus `## claims` -- the time cycle's consolidated view), plus `remind` — the opt-in per-turn one-line reminder, ENFORCEMENT §4). Keep the entry point, `scripts/sdlc_core.py` and `scripts/knowledge.py` together; the last supplies common memory and KB integrity.
|
|
28
28
|
- `scripts/docs_site.py --root <project> --open` (`--zip <file>` to share): opens in the user's browser a read-only site of what the agent reads; use it to show the project state.
|
|
29
29
|
It is what users mean by "docs site", "minisito", "documentation site" or "project site": run it, never build a page by hand.
|
|
30
30
|
- `ENFORCEMENT.md`: optional setup for CI and hooks.
|
|
@@ -102,7 +102,9 @@ Triage decides IF documentation is due; this table decides WHICH document each e
|
|
|
102
102
|
| `topics/<slug>.md` | A placement verdict creates it (MISSING/INADEQUATE-child, `taxonomy.md`); reconciliation updates its claim rows. One node per topic — a similar-but-distinct concept is a sibling with `related:` + a written distinction, never a merge and never a duplicate. Merged/renamed nodes become tombstones (`status: SUPERSEDED` + `redirect_to:`), never deleted. | 4 |
|
|
103
103
|
| a KB bundle (`export`) | Knowledge must leave this project. Export is L1 — it writes nothing into the corpus. **Importing one is L3**: a source enters the corpus and nodes are created, and a bundle is external input, so never L1 whatever its size (`portability.md`). | — |
|
|
104
104
|
| `corpus/given/*` + sidecar | A source arrives: it becomes a content-addressed artifact with a sidecar carrying digest/date/`supersedes:`. A text source is copied verbatim, except that credentials and unneeded personal or confidential data are redacted first (`redacted_at_intake:`). A non-text source yields its stored canonical extraction — copied **beside** the original when that is small enough to keep, or **instead of it** on a large binary corpus, where the original stays where it lives and is recorded as `original_path:`/`original_sha256:` (`distillation.md` §1, which owns this rule) — never copied beside, and no `original_sha256:`, when it was redacted. Never edited after ingest — the digest check on whatever `given/` holds is what enforces it; the one removal is withdrawing an artifact that holds a secret (`distillation.md` §1). | 4 |
|
|
105
|
+
| `corpus/given/*` + sidecar `provenance: DECLARED` | A document leaves the project — to a client, a supplier, another function: the version sent is ingested like any source (redaction included), its sidecar carrying `provenance: DECLARED`, `recipient:` (an organization or role) and `date:` = the day it was sent; its claims are `DECLARED`. A resend is a new artifact that `supersedes:` the earlier one. Drafts, internal working documents and the files a sent document is generated from do not enter (`distillation.md` §1). | 4 |
|
|
105
106
|
| `corpus/notes/*` | Something is said (`origin: elicited`), synthesised (`derived_from:`), or ruled (`basis:`). A note with none of the three is refused by the validator. | 4 / 5 |
|
|
107
|
+
| `audit/INVENTORY_exclusions.md` | The practitioner approves that some project files never enter the corpus (code, copies, generated files): one row `pattern \| reason \| approved`, written only after the approval, dated that day. `validate`/`check` refuse a malformed row. Read by `inventory`; never a list of what to ingest next (`vision/rulings.md` r37). | 4 |
|
|
106
108
|
| the day's decisions, at a user-signed closing (the Capture Moment sweep) | Verification that the phase-4 notes/claims exist; NEW `corpus/notes/*` + claim rows only for what the sweep surfaces un-captured. The event is the CLOSING, disjoint from the row above (which fires when something is SAID). | 5 / session end |
|
|
107
109
|
| `solutions/PLAN_[topic].md` + its ledger | A source too long for one context is ingested: **one task per reading window** (30 pages by default; the plan states the window used), each ending at a declared `extracted_through:`. That ledger is the register the ingestion resumes from across sessions — ingestion never builds a second one (`distillation.md` §3, `templates.md`). | 4 |
|
|
108
110
|
| `corpus/given/*.meta.md` — `extracted_through:` | Every reading window closes by advancing it (`p=<n>`, `L<n>`, `complete`). Required once any claim cites the artifact: unstated, "I am finished" cannot be falsified, which is how a sampled 200-page manual passes for an ingested one. | 4 |
|
|
@@ -201,6 +203,14 @@ twin of the guide consult above.
|
|
|
201
203
|
- `kb: <slug>[, <slug> …] → node matched, no claims` — surface the node's `gaps:` when
|
|
202
204
|
it answers the question: "the KB knows it doesn't know" is a first-class answer.
|
|
203
205
|
- `kb: no coverage` — index read, nothing fits.
|
|
206
|
+
Before declaring `no coverage` or `node matched, no claims`, look where the KB has
|
|
207
|
+
not: run `inventory <path>` on the project paths whose names plausibly hold the
|
|
208
|
+
subject, count its `NOT_EXAMINED` lines, and say it in the verdict —
|
|
209
|
+
`kb: no coverage; not examined under <path>: N` (N = 0 stated), naming the file when
|
|
210
|
+
one plainly holds the subject. When no path plausibly holds it, never run
|
|
211
|
+
`inventory .`: cite orient's bound (`not examined in the project: <= N`). When
|
|
212
|
+
`inventory` fails, the clause says so. A corpus is a subset of the project; a
|
|
213
|
+
verdict that hides the residue certifies a lookup over what was never read.
|
|
204
214
|
- `kb: index absent — regenerate (sdlc_check.py index)` — the INDEX is missing, not
|
|
205
215
|
necessarily the graph (nodes routinely exist while the index lags); regenerate and
|
|
206
216
|
rescan.
|
|
@@ -216,7 +226,11 @@ twin of the guide consult above.
|
|
|
216
226
|
decision-grounding** — surfaced, never silently cited. Per-provenance stance: GIVEN's
|
|
217
227
|
source column is already the direct citation; ELICITED/RULING carry the practitioner's
|
|
218
228
|
own authority; an IMPORTED claim grounding a decision is named as un-re-ratified
|
|
219
|
-
foreign authority.
|
|
229
|
+
foreign authority. A **DECLARED** claim is what this project told a recipient: cite it
|
|
230
|
+
as "declared to <recipient> on <date>", never as a fact about the product, and a
|
|
231
|
+
`DIVERGES` row together with the ledger row it diverges from. A DECLARED artifact is
|
|
232
|
+
**not ground** for a product decision — a chain that ends on one rests on our own
|
|
233
|
+
statement: say so and treat the claim as unverified for decision-grounding.
|
|
220
234
|
- **Citation scope — the row's test is scope, not fidelity.** Before citing a claim in
|
|
221
235
|
an answer or a document, verify every part of the row belongs to the question: a row
|
|
222
236
|
can be atomic as provenance and plural as meaning (`distillation.md` §3), and a
|
|
@@ -29,6 +29,7 @@ from the corpus, so everything is re-derivable when the rules improve.
|
|
|
29
29
|
| a file from the practitioner | `corpus/given/<name>-<hash8>.<ext>` — verbatim, **content-addressed with a raw-byte sha256**; a newer version is a new file whose sidecar says `supersedes: <old>` — never an overwrite |
|
|
30
30
|
| a non-text original (PDF, docx, xlsx) | its **canonical extraction**: `corpus/given/<name>-<hash8>.txt`, pages separated by form-feed; extractor id, version and normalization recorded in the sidecar. Offsets address THIS file — stored bytes, not a runtime step. The original is copied beside it when it is small enough to be worth keeping; **on a large or binary-heavy corpus the extraction is the artifact and the original stays where it is** (below) |
|
|
31
31
|
| something the practitioner says | a transcription in `corpus/notes/`, frontmatter `origin: elicited`, dated |
|
|
32
|
+
| a document the project sends out | the version sent, in `corpus/given/` like any source (redacted at intake), sidecar `provenance: DECLARED`, `recipient:` (an organization or role, never a private person's name) and `date:` = the day sent; when the file also lives in the project, `original_path:` names it. Its claims are `DECLARED`: what we said, to whom, when — not facts about the product. A resend `supersedes:` the earlier send; drafts and the files a document is generated from never enter |
|
|
32
33
|
| an agent synthesis | a note in `corpus/notes/` with `derived_from:` listing its sources — a note with neither `origin:` nor `derived_from:` nor `basis:` is **model knowledge disguised as a source**, and the validator refuses it |
|
|
33
34
|
| a practitioner ruling | a note with `basis:` (`reconciliation.md`) |
|
|
34
35
|
|
|
@@ -88,8 +89,13 @@ Say the limit out loud, because a field that looks like a guarantee and is not i
|
|
|
88
89
|
than an absent one — and say it **per field**, because the two are not in the same
|
|
89
90
|
position:
|
|
90
91
|
|
|
91
|
-
- **`original_sha256` is
|
|
92
|
-
|
|
92
|
+
- **`original_sha256` is checked only where the original is local.** `stale` and
|
|
93
|
+
`inventory` hash the file `original_path:` resolves to, when it is a local regular
|
|
94
|
+
file, and report an original that changed since ingest (`stale`'s `## claims`, until
|
|
95
|
+
a successor sidecar `supersedes:` this one). `check` never does: it runs on every
|
|
96
|
+
change, and re-reading gigabytes there is not proportional. Elsewhere — a remote, a
|
|
97
|
+
cloud-only file, no path — it detects nothing; it lets a human re-verify by hand and
|
|
98
|
+
it dates the ingest.
|
|
93
99
|
Same honesty as a guide's `source_hash`, which proves the source is unchanged and
|
|
94
100
|
never that the guide described it correctly.
|
|
95
101
|
- **`original_path` IS checked — for resolution only.** "We do not hold it" was never a
|
|
@@ -167,10 +173,14 @@ Claims live in the owning topic's `## Claims` table (`templates.md` has the temp
|
|
|
167
173
|
`p=<n>@<start>-<end>` (character offsets into page n of the stored extraction),
|
|
168
174
|
`L<a>-<b>` (line files), `Sheet<s>!<cell>`. The span must exist — the validator opens
|
|
169
175
|
the file and checks.
|
|
170
|
-
- **prov** — `GIVEN | ELICITED | DERIVED | RULING | IMPORTED
|
|
176
|
+
- **prov** — `GIVEN | ELICITED | DERIVED | RULING | IMPORTED | DECLARED`. `DECLARED`
|
|
177
|
+
rests only on a document this project sent (its sidecar says so and names the
|
|
178
|
+
`recipient:`), and no other provenance may cite one, in any source position — a
|
|
179
|
+
declaration is never corroboration (`reconciliation.md` §1). `IMPORTED` is a ruling that came from another project (`portability.md`): it keeps its text and original `basis:`, it rests on a renamed copy of its note that says `imported_from:`, `import` reopens the set it had settled, and nothing is settled by it until you ratify it with a ruling of your own. Information for whoever resolves a
|
|
171
180
|
conflict; never a rank.
|
|
172
|
-
- **state** — `OK`, `CONTESTED <id>[,..]`, `SUPERSEDED <id>[,..]
|
|
173
|
-
owns the transitions; a split bundle
|
|
181
|
+
- **state** — `OK`, `CONTESTED <id>[,..]`, `SUPERSEDED <id>[,..]`, and on a DECLARED row
|
|
182
|
+
only `DIVERGES <id>[,..]` (`reconciliation.md` owns the transitions; a split bundle
|
|
183
|
+
names ALL its successors).
|
|
174
184
|
|
|
175
185
|
## 3. Extraction discipline
|
|
176
186
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
var D = null; // documents, loaded on first use
|
|
13
13
|
var K = null; // knowledge base, loaded on first use
|
|
14
14
|
var app = document.getElementById("app");
|
|
15
|
-
var ORIGINS = ["given", "elicited", "derived", "ruling", "imported"];
|
|
15
|
+
var ORIGINS = ["given", "elicited", "derived", "ruling", "imported", "declared"];
|
|
16
16
|
|
|
17
17
|
// ------------------------------------------------------------- helpers
|
|
18
18
|
|
|
@@ -483,6 +483,7 @@
|
|
|
483
483
|
if (f.statement) marks.push(el("span", { "class": "tag statement", title: t("statement_notice"), text: t("statement_mark") }));
|
|
484
484
|
if (f.state === "superseded") marks.push(el("span", { "class": "tag replaced", title: t("state_superseded_why"), text: t("state_superseded") }));
|
|
485
485
|
if (f.state === "contested") marks.push(el("span", { "class": "tag conflict", title: t("state_contested_why"), text: t("state_contested") }));
|
|
486
|
+
if (f.state === "diverges") marks.push(el("span", { "class": "tag conflict", title: t("state_diverges_why"), text: t("state_diverges") }));
|
|
486
487
|
if (opts && opts.topic) marks.push(link("#/topic/" + encodeURIComponent(f.topic), f.topic, "chip"));
|
|
487
488
|
return el("li", { "class": "fact" + (f.state === "superseded" ? " replaced" : ""), onclick: function () { location.hash = "#/fact/" + encodeURIComponent(id); } },
|
|
488
489
|
el("span", { "class": "dot bg-" + oc }),
|
|
@@ -597,6 +598,11 @@
|
|
|
597
598
|
el("p", { "class": "why-line", text: t("state_contested_why") }),
|
|
598
599
|
el("ul", { "class": "facts" }, (f.with || []).map(function (s) { return factLine(s, { topic: true }); })));
|
|
599
600
|
}
|
|
601
|
+
if (f.state === "diverges") {
|
|
602
|
+
parts.push(el("h3", null, el("span", { "class": "tag conflict", text: t("state_diverges") }), " ", t("fact_diverges_from")),
|
|
603
|
+
el("p", { "class": "why-line", text: t("state_diverges_why") }),
|
|
604
|
+
el("ul", { "class": "facts" }, (f.diverges_from || []).map(function (s) { return factLine(s, { topic: true }); })));
|
|
605
|
+
}
|
|
600
606
|
if (f.grounds) {
|
|
601
607
|
parts.push(el("h3", { text: t("fact_rests_on") }),
|
|
602
608
|
el("p", { "class": "why-line", text: t("ground_class_" + f.ground_class) }),
|
|
@@ -56,7 +56,9 @@ window.DOCS_SITE_STRINGS = {
|
|
|
56
56
|
event_request: "request record",
|
|
57
57
|
event_note: "note",
|
|
58
58
|
event_source: "source document received",
|
|
59
|
+
event_sent: "document sent",
|
|
59
60
|
event_review: "review",
|
|
61
|
+
event_defect: "defect reported after merge",
|
|
60
62
|
event_decision: "decision record",
|
|
61
63
|
event_project_note: "project note",
|
|
62
64
|
event_spike: "exploration outcome",
|
|
@@ -231,6 +233,8 @@ window.DOCS_SITE_STRINGS = {
|
|
|
231
233
|
origin_ruling_why: "Taken to settle a conflict.",
|
|
232
234
|
origin_imported: "from another knowledge base",
|
|
233
235
|
origin_imported_why: "Brought in from another knowledge base, where its own sources are.",
|
|
236
|
+
origin_declared: "declared by this project",
|
|
237
|
+
origin_declared_why: "What this project told a recipient, on the day it was sent: a statement of ours, not a fact about the product.",
|
|
234
238
|
origin_other: "origin as written: {v}",
|
|
235
239
|
origin_other_why: "An origin this site does not know.",
|
|
236
240
|
|
|
@@ -239,6 +243,8 @@ window.DOCS_SITE_STRINGS = {
|
|
|
239
243
|
state_superseded_why: "A newer fact takes its place.",
|
|
240
244
|
state_contested: "in conflict",
|
|
241
245
|
state_contested_why: "Facts that disagree, not yet settled.",
|
|
246
|
+
state_diverges: "diverges",
|
|
247
|
+
state_diverges_why: "What we declared contradicts what the project decided. It is corrected by a new send, or the decision changes.",
|
|
242
248
|
|
|
243
249
|
statement_mark: "statement",
|
|
244
250
|
statement_prefix: "[statement] {text}",
|
|
@@ -254,6 +260,7 @@ window.DOCS_SITE_STRINGS = {
|
|
|
254
260
|
fact_replaced_by: "Replaced by",
|
|
255
261
|
fact_current_at_end: "Current fact at the end of each chain",
|
|
256
262
|
fact_in_conflict_with: "In conflict with",
|
|
263
|
+
fact_diverges_from: "Diverges from",
|
|
257
264
|
fact_rests_on: "What it rests on",
|
|
258
265
|
fact_untraced: "This part could not be traced.",
|
|
259
266
|
fact_sources: "Sources",
|
|
@@ -266,6 +273,7 @@ window.DOCS_SITE_STRINGS = {
|
|
|
266
273
|
fact_unknown: "This fact is not in this knowledge base.",
|
|
267
274
|
|
|
268
275
|
ground_document: "a document",
|
|
276
|
+
ground_declared: "a document this project sent",
|
|
269
277
|
ground_statement: "a person's statement",
|
|
270
278
|
ground_decision: "a decision",
|
|
271
279
|
ground_kb: "another knowledge base",
|
|
@@ -273,6 +281,7 @@ window.DOCS_SITE_STRINGS = {
|
|
|
273
281
|
ground_class_statement: "Rests, at least in part, on a person's statement.",
|
|
274
282
|
ground_class_untraceable: "Part of what it rests on could not be traced.",
|
|
275
283
|
ground_class_imported: "Rests on another knowledge base.",
|
|
284
|
+
ground_class_declared: "Rests on our own declaration to a recipient, not on evidence.",
|
|
276
285
|
ground_class_documents: "Rests on documents or decisions only.",
|
|
277
286
|
|
|
278
287
|
chain_loop: "The chain of replacements loops back to {id}: it stops here.",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
--o-derived: #a99be0;
|
|
19
19
|
--o-ruling: #7fa7e6;
|
|
20
20
|
--o-imported: #9cc96b;
|
|
21
|
+
--o-declared: #d98fb0;
|
|
21
22
|
--conflict: #e0605a;
|
|
22
23
|
--display: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Charter, Georgia, serif;
|
|
23
24
|
--body: "Segoe UI Variable Text", "Avenir Next", Avenir, "Gill Sans Nova", "Gill Sans", "Trebuchet MS", sans-serif;
|
|
@@ -179,6 +180,7 @@ a.factid code { color: var(--link); }
|
|
|
179
180
|
.o-derived { color: var(--o-derived); } .bg-derived { background: var(--o-derived); }
|
|
180
181
|
.o-ruling { color: var(--o-ruling); } .bg-ruling { background: var(--o-ruling); }
|
|
181
182
|
.o-imported { color: var(--o-imported); } .bg-imported { background: var(--o-imported); }
|
|
183
|
+
.o-declared { color: var(--o-declared); } .bg-declared { background: var(--o-declared); }
|
|
182
184
|
.o-other { color: var(--paper-3); } .bg-other { background: var(--paper-3); }
|
|
183
185
|
|
|
184
186
|
.panel {
|
|
@@ -262,6 +262,12 @@ in the snapshot, never to your assumption about what the code does.
|
|
|
262
262
|
(source-anchored), not an elaborated version of it.
|
|
263
263
|
- When in doubt about whether something is "supported" by the source, treat it
|
|
264
264
|
as not covered rather than stretching the marker to fit.
|
|
265
|
+
- **Declared exception: `## Preservation constraints`.** A code guide may carry
|
|
266
|
+
this section, copied at an L3 closure from the ANALYSIS's table (code lens,
|
|
267
|
+
`SKILL.md` Phase 5). The section's marker cites each row it holds as
|
|
268
|
+
`[source: solutions/ANALYSIS_<name>.md#P<n>]` instead of a snapshot anchor: each
|
|
269
|
+
row records what the code must keep doing, as that unit enumerated and probed it,
|
|
270
|
+
not an excerpt of the code.
|
|
265
271
|
|
|
266
272
|
## 4. Ingestion bound (T9)
|
|
267
273
|
|
|
@@ -293,5 +299,7 @@ state it explicitly when handing off a newly created guide.
|
|
|
293
299
|
closure (docs travel with the code); when you merely READ one, check it against
|
|
294
300
|
the code before trusting it. Too large to absorb → mark it `SUPERSEDED` rather
|
|
295
301
|
than leave a confident-wrong map badged CURRENT, which is worse than none.
|
|
302
|
+
- **A regenerated guide keeps `## Preservation constraints` unchanged**: the
|
|
303
|
+
section does not come from the snapshot (§3), so regeneration carries it over.
|
|
296
304
|
- **Agent-global KB guides** use the same pipeline and validator via
|
|
297
305
|
`--root ~/.agentic-sdlc`; freshness via the same `stale` engine.
|
|
@@ -39,7 +39,8 @@ sdlc_check.py check # always, right after
|
|
|
39
39
|
and, if a topic was skipped, *"a topic with the same name already exists; I have not
|
|
40
40
|
merged them, because they may not be the same subject. Do you want me to look?"*
|
|
41
41
|
4. If an imported ruling arrived: *"one decision came from the other project. I have kept
|
|
42
|
-
it, marked as theirs, and it cannot settle anything here until you confirm it.
|
|
42
|
+
it, marked as theirs, and it cannot settle anything here until you confirm it. The
|
|
43
|
+
question it had closed is open again here, with their decision beside the sources."*
|
|
43
44
|
|
|
44
45
|
Do not explain the closure, the id hashing or the provenance model unless asked. The
|
|
45
46
|
user asked to move their knowledge, not to learn how it is stored.
|
|
@@ -74,6 +75,11 @@ You choose topics; the export decides what must travel with them.
|
|
|
74
75
|
When a partner row lives in an unselected topic, that topic is **added and reported** —
|
|
75
76
|
never dropped, never silently.
|
|
76
77
|
- **Not the `not-yet-honored-by` markers.** They describe this project's code, so the export drops them and says so, without a number.
|
|
78
|
+
- **Not the declarations.** A `DECLARED` row is this project's commitment to its own
|
|
79
|
+
recipient; carried elsewhere it would read as the importer's. The export leaves the
|
|
80
|
+
rows and the artifacts only they cite behind and says so in one line, without a
|
|
81
|
+
number. Nothing else is lost: no other row may point at a declaration, and the export
|
|
82
|
+
refuses one that does (run `check`).
|
|
77
83
|
|
|
78
84
|
If a conflict partner resolves to no row at all, the export **refuses**: exporting a
|
|
79
85
|
broken set is worse than exporting nothing.
|
|
@@ -83,7 +89,8 @@ tombstone sidecar stays home.
|
|
|
83
89
|
|
|
84
90
|
## 3. Import is additive, and decides nothing
|
|
85
91
|
|
|
86
|
-
`import` writes files. It does not place concepts, merge bodies, or settle disagreements
|
|
92
|
+
`import` writes files. It does not place concepts, merge bodies, or settle disagreements —
|
|
93
|
+
and it refuses to let the bundle settle them either: a ruling arrives foreign (§4).
|
|
87
94
|
|
|
88
95
|
- **Never overwrites a topic.** A slug that already exists is reported and skipped: two
|
|
89
96
|
projects using the word `pricing` may mean two different things, and that judgement is
|
|
@@ -96,7 +103,12 @@ tombstone sidecar stays home.
|
|
|
96
103
|
It refuses on: a missing or unmarked `MANIFEST.md`; a path that escapes the docs root; an
|
|
97
104
|
artifact whose name matches an existing one **with different bytes** (content-addressed
|
|
98
105
|
names must mean equal content — a mismatch means one of the two is lying about its
|
|
99
|
-
origin); an incomplete conflict set
|
|
106
|
+
origin); an incomplete conflict set — a `CONTESTED` or `SUPERSEDED` pointer that would
|
|
107
|
+
resolve to no row, or to a row of this project that does not name it back (an import
|
|
108
|
+
never edits an existing topic); a row settled by a ruling the import does not write, or
|
|
109
|
+
by a row this project holds as `IMPORTED`; a ruling it cannot carry foreign (§4); a
|
|
110
|
+
`DECLARED` row (an export never ships one, so the bundle was edited by hand). Each
|
|
111
|
+
refusal names the row and writes nothing.
|
|
100
112
|
|
|
101
113
|
After importing, run `check`. The import is deliberately not a validator.
|
|
102
114
|
|
|
@@ -108,19 +120,43 @@ another owner's decision, and importing it unchanged would make that decision bi
|
|
|
108
120
|
here without anyone here granting it — the machine deciding, which reconciliation
|
|
109
121
|
refuses everywhere else.
|
|
110
122
|
|
|
111
|
-
So
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
So `import` makes every `RULING` row of a topic it writes arrive as **`prov: IMPORTED`**
|
|
124
|
+
(owner rulings 2026-08-03 and 2026-10-23):
|
|
125
|
+
|
|
126
|
+
- **its note arrives as a renamed copy**, `corpus/notes/imported_<source project>_<name>`,
|
|
127
|
+
whose frontmatter adds `imported_from: <source project> — <original path>` and reads
|
|
128
|
+
`origin: imported`. Its text and original `basis:` travel **verbatim** — the knowledge
|
|
129
|
+
is not lost, and pretending it is a `DERIVED` synthesis would make the row lie about
|
|
130
|
+
where it came from. The note is never edited under its own name: a line inserted into
|
|
131
|
+
it would move the line every locator cites. The row cites the copy at the same text
|
|
132
|
+
(its locator and id move with it), and a bundle note `derived_from:` the ruling names
|
|
133
|
+
the copy. The original name is never written, so a row copied by hand out of a skipped
|
|
134
|
+
topic cannot cite a foreign note as local;
|
|
135
|
+
- **the question it had closed reopens.** The rows it superseded and the ruling itself
|
|
136
|
+
arrive as one `CONTESTED` set; a ruling already contested with a newer source keeps
|
|
137
|
+
that partner, one that superseded nothing arrives `OK`;
|
|
138
|
+
- the validator refuses an `IMPORTED` row whose note lacks `imported_from:` (unnamed,
|
|
139
|
+
the class says nothing), a `SUPERSEDED <id>` pointing at an `IMPORTED` row, and a
|
|
140
|
+
`RULING` row on a note that names a foreign origin.
|
|
141
|
+
|
|
142
|
+
**Ratification** is the ordinary ruling of `reconciliation.md`, and it is you deciding,
|
|
143
|
+
which is the only thing that ever settles anything here: write your own note with your
|
|
144
|
+
own `basis:`, add a `RULING` row on it, and mark every row `CONTESTED` with the imported
|
|
145
|
+
one — following the sets transitively — `SUPERSEDED <your id>`, the `IMPORTED` row
|
|
146
|
+
included. Relabelling the `IMPORTED` row `RULING` is refused: another project's note
|
|
147
|
+
cannot carry your decision.
|
|
148
|
+
|
|
149
|
+
Any bundle note that declares a `basis:` lands this way, renamed, even when its ruling row
|
|
150
|
+
does not travel with it — otherwise it would land first under its own name and a later
|
|
151
|
+
import of its ruling would find it there. A ruling `import` cannot carry this way is
|
|
152
|
+
refused, never copied: one not resting on a note cited by line, on a note with no
|
|
153
|
+
frontmatter, or on a note that already names a foreign origin (the source relabelled an
|
|
154
|
+
imported row); a row superseded both by a ruling and by a row that is not one; a ruling
|
|
155
|
+
another row superseded while rows still depend on it. A tree imported before this
|
|
156
|
+
conversion existed (≤ kb 1.31.0) holds the foreign ruling as `RULING` on its original
|
|
157
|
+
note: delete the imported topic and its files and import again, or convert it by hand as
|
|
158
|
+
above. The origin is named after the exporting folder (`source_project`), so two clones
|
|
159
|
+
of one project in differently named folders name it differently.
|
|
124
160
|
|
|
125
161
|
## 5. Triage
|
|
126
162
|
|
|
@@ -24,6 +24,15 @@ Subject-sameness is semantic judgement; no query performs it. Classify:
|
|
|
24
24
|
| **coexistence** | contradictory only if scopes overlapped — and by the half-open rule they do not | both rows stay `OK`. "12k until March" and "15k from March" are two truths, not a conflict |
|
|
25
25
|
| **conflict** | contradictory, scopes overlap | **every** row in the set becomes `CONTESTED` listing all counterparts. Nothing is picked |
|
|
26
26
|
|
|
27
|
+
**A declaration is not a source.** A `DECLARED` row (`distillation.md` §1: what this
|
|
28
|
+
project sent) never joins the five outcomes with a fact: it never corroborates a row,
|
|
29
|
+
never refines or supersedes one, never contests one. Against the ledger it is either
|
|
30
|
+
consistent (`OK`) or it **diverges**: its assertion contradicts a non-superseded,
|
|
31
|
+
non-DECLARED row, scopes overlapping → the DECLARED row alone becomes
|
|
32
|
+
`DIVERGES <id>[, ..]`; the ledger row is not touched, because our own wrong statement
|
|
33
|
+
puts nothing about the product in doubt. Two declarations that contradict each other
|
|
34
|
+
are `CONTESTED` — both are still held by their recipients.
|
|
35
|
+
|
|
27
36
|
The machine then verifies (the `graph` check): ids recompute, sources resolve and their
|
|
28
37
|
spans exist, `CONTESTED` is **symmetric** (editing one cell to `OK` fails the check —
|
|
29
38
|
the cheapest laundering), a `CONTESTED` pointer at a `SUPERSEDED` row is an error,
|
|
@@ -73,6 +82,9 @@ premise: signed amendments arrive later.
|
|
|
73
82
|
| newer source vs a ruling | new set {new row, ruling row}; escalation shows the `basis:` |
|
|
74
83
|
| member refined | the refining row inherits the member's contested relations (re-judged); counterparts rewritten to the new id |
|
|
75
84
|
| corroboration of a contested member | source appended; the set untouched — more evidence is not new information, and the escalation form shows source counts |
|
|
85
|
+
| a declaration contradicts the ledger | the DECLARED row → `DIVERGES <ledger ids>`; the ledger rows stay as they are; escalation names it |
|
|
86
|
+
| a new send corrects it | the new DECLARED row; the divergent one → `SUPERSEDED <new-id>` (a declaration is superseded only by a later declaration) |
|
|
87
|
+
| a ruling changes the decision | the ledger row → `SUPERSEDED <ruling-id>`; `check` then refuses the stale `DIVERGES` until the declaration is re-judged against the ruling: `OK` if it now agrees, `DIVERGES <ruling-id>` if not |
|
|
76
88
|
| bundled row split (`distillation.md` §3) | N atomic rows `OK`, each on its narrowed span; the bundle → `SUPERSEDED <id1>[, <id2> ..]`, text intact; contested relations re-judged per successor |
|
|
77
89
|
|
|
78
90
|
A superseded row keeps its `not-yet-honored-by` marker with its text. Copy the marker to each successor that still states behavior the code does not have; the analysis or shadow citing the predecessor's id satisfies the successor.
|
|
@@ -103,7 +115,11 @@ record like any claim-table edit.
|
|
|
103
115
|
Escalations are presented **once, at the end of a run** — the pipeline never stops
|
|
104
116
|
mid-ingest to ask. Each escalation names: the claims in the set, each one's source
|
|
105
117
|
(reopenable), date and provenance, and one line on why the machine cannot decide
|
|
106
|
-
("same subject, overlapping validity, GIVEN vs GIVEN, no newer source").
|
|
118
|
+
("same subject, overlapping validity, GIVEN vs GIVEN, no newer source"). A divergence
|
|
119
|
+
is escalated the same way, naming the recipient and the day it was sent: the
|
|
120
|
+
practitioner corrects it with a new send or changes the decision. One limit stated
|
|
121
|
+
out loud: `DIVERGES` is one-way, so unlike `CONTESTED` a hand edit back to `OK` has
|
|
122
|
+
no counterpart to fail on — the diff review is what catches it. **A question
|
|
107
123
|
that cannot fill this form is not askable** — it is the symptom that the answer is in
|
|
108
124
|
the corpus and was not searched. Generic confirmations are not questions.
|
|
109
125
|
|
|
@@ -122,10 +138,15 @@ An `IMPORTED` row is a ruling made by the owner of a different project
|
|
|
122
138
|
|
|
123
139
|
- It keeps its text, its span and its original `basis:` verbatim — the knowledge is not
|
|
124
140
|
lost, and relabelling it `DERIVED` would make the row lie about its origin.
|
|
125
|
-
-
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
141
|
+
- It rests on a renamed copy of its note (`imported_<project>_<name>`) that carries
|
|
142
|
+
`imported_from:`. Unnamed, the class says nothing and the row is a `RULING` with the
|
|
143
|
+
label filed off; the validator refuses it.
|
|
144
|
+
- **It cannot settle anything here.** `import` reopens the set it had settled: its former
|
|
145
|
+
members and the ruling arrive `CONTESTED` with each other. `SUPERSEDED <id>` pointing at
|
|
146
|
+
an `IMPORTED` row is an error: nobody in this project granted that decision its
|
|
147
|
+
authority.
|
|
148
|
+
|
|
149
|
+
Ratification is the ruling procedure above, and it is you deciding: your own note with
|
|
150
|
+
your own `basis:`, a `RULING` row on it, and every row `CONTESTED` with the imported one —
|
|
151
|
+
transitively, when it sits in more than one set — `SUPERSEDED <your id>`, the imported row
|
|
152
|
+
included. A `RULING` on a note that names a foreign origin is refused.
|
|
@@ -187,7 +187,10 @@ can block forever gets removed. **Log one row per completed review, PASS or FAIL
|
|
|
187
187
|
logging only passes would erase exactly that evidence. The row goes in
|
|
188
188
|
`ai_docs/audit/reviews/REVIEW_LOG.md` (create it if absent — `templates.md`):
|
|
189
189
|
`| date | doc_key | tier | model | reviewer | findings_raised | findings_real | verdict | revise_rounds |`,
|
|
190
|
-
with `tier` = `design` or `closure` in Standalone.
|
|
190
|
+
with `tier` = `design` or `closure` in Standalone. In the code lens, a wrong behaviour
|
|
191
|
+
the owner reports in a merged L3 change is one row too, tier `post-merge`, `model`
|
|
192
|
+
`none (owner report)`, reviewer `owner`, the owner quoted in the notes: a defect
|
|
193
|
+
record, not a review, so `benefit` keeps it out of every review figure. **`model` records the capability
|
|
191
194
|
tier that actually ran** — `deep`, `light`, `economy`, `single (client exposes no
|
|
192
195
|
choice)`, `below floor: <reason>`, or `none (not due)` on a NOT DUE row — never a provider name in doctrine (a project
|
|
193
196
|
may of course write its own model names in its own log). Without it the floor above
|
|
@@ -285,6 +288,11 @@ The binding inputs, by review type:
|
|
|
285
288
|
clause reads both), and the **probe harness** (`ai_docs/solutions/harness_[feature]/`).
|
|
286
289
|
Hand these *in addition to* the design artifact — the reviewer checks the artifact
|
|
287
290
|
**against** them, not only for internal consistency.
|
|
291
|
+
- **The constraint view comes last.** Where the ANALYSIS carries ids, the reviewer
|
|
292
|
+
derives the callers and behaviours first, then compares with
|
|
293
|
+
`sdlc_check.py constraints --unit <id>`: a constraint the design states and the
|
|
294
|
+
diff breaks is a finding. The view never replaces the ANALYSIS or the diff in the
|
|
295
|
+
request.
|
|
288
296
|
- **For a closure review, the diff and the test evidence**: the run output that
|
|
289
297
|
backs every completion claim, dated after the final relevant change.
|
|
290
298
|
- **For a design or closure review of an L3/E3 unit, the request record**
|
|
@@ -615,6 +615,11 @@ class Grounds:
|
|
|
615
615
|
return [{"kind": "untraceable", "path": rel}]
|
|
616
616
|
seen = seen | {rel}
|
|
617
617
|
if rel.startswith("corpus/given/"):
|
|
618
|
+
# F-084: a document this project SENT is our own declaration, not
|
|
619
|
+
# ground for a fact about the product.
|
|
620
|
+
side = self._meta(rel + ".meta.md")
|
|
621
|
+
if _v(side, "provenance").upper() == "DECLARED":
|
|
622
|
+
return [{"kind": "declared", "path": rel}]
|
|
618
623
|
return [{"kind": "document", "path": rel}]
|
|
619
624
|
if not _inside(self.docs, rel) or not (self.docs / rel).is_file():
|
|
620
625
|
return [{"kind": "untraceable", "path": rel}]
|
|
@@ -643,6 +648,8 @@ def ground_class(leaves):
|
|
|
643
648
|
return "statement"
|
|
644
649
|
if "untraceable" in kinds:
|
|
645
650
|
return "untraceable"
|
|
651
|
+
if "declared" in kinds:
|
|
652
|
+
return "declared"
|
|
646
653
|
if "kb" in kinds:
|
|
647
654
|
return "imported"
|
|
648
655
|
return "documents"
|
|
@@ -707,12 +714,15 @@ def build_kb(docs):
|
|
|
707
714
|
kind, rest = _kind(r)
|
|
708
715
|
f = {"text": r["claim"], "valid": r["valid"], "qty": r["qty"], "about": r["about"],
|
|
709
716
|
"origin": (r["prov"] or "").strip().lower(), "topic": slug,
|
|
710
|
-
"state": {"SUPERSEDED": "superseded", "CONTESTED": "contested"
|
|
717
|
+
"state": {"SUPERSEDED": "superseded", "CONTESTED": "contested",
|
|
718
|
+
"DIVERGES": "diverges"}.get(kind, "ok")}
|
|
711
719
|
if kind == "SUPERSEDED":
|
|
712
720
|
f["successors"] = _ids(rest)
|
|
713
721
|
f["chain_ends"] = chain_ends(r["id"], by_id)
|
|
714
722
|
if kind == "CONTESTED":
|
|
715
723
|
f["with"] = _ids(rest)
|
|
724
|
+
if kind == "DIVERGES":
|
|
725
|
+
f["diverges_from"] = _ids(rest)
|
|
716
726
|
srcs = []
|
|
717
727
|
for src in r["source"].split(";"):
|
|
718
728
|
src = src.strip()
|
|
@@ -1246,7 +1256,9 @@ def chronology(docs, pages):
|
|
|
1246
1256
|
text = _text(docs / rel) if (docs / rel).is_file() else None
|
|
1247
1257
|
if text is not None:
|
|
1248
1258
|
for row in sdlc_core.parse_review_log(text)[0]:
|
|
1249
|
-
|
|
1259
|
+
# F-085: a `post-merge` row is the owner's defect report, not a review
|
|
1260
|
+
kind = "defect" if row["tier"].lower().startswith("post-merge") else "review"
|
|
1261
|
+
add(row["date"], kind, "%s — %s: %s" % (
|
|
1250
1262
|
row["doc_key"], row["tier"], row["verdict"]), rel)
|
|
1251
1263
|
for p in sorted(docs.glob("architecture/ADR_*.md")):
|
|
1252
1264
|
rel = "architecture/" + p.name
|
|
@@ -1269,7 +1281,9 @@ def chronology(docs, pages):
|
|
|
1269
1281
|
continue
|
|
1270
1282
|
if kind == "source":
|
|
1271
1283
|
name = p.name[:-len(suffix)]
|
|
1272
|
-
|
|
1284
|
+
# F-084: a DECLARED sidecar's date is the day it was SENT
|
|
1285
|
+
sent = _v(fm, "provenance").upper() == "DECLARED"
|
|
1286
|
+
add(date, "sent" if sent else kind, name, rel, source=name)
|
|
1273
1287
|
else:
|
|
1274
1288
|
add(date, kind, title_of(rel, text), rel)
|
|
1275
1289
|
# Most recent first. Within one day a document's later entry is the more recent
|