Repository navigation
Add typed knowledge operations with a rule lifecycle and the L1 gate - #204
Conversation
The v1 curation path can only append a rule, so nothing could ever retire, supersede or delete one, and the SDK never wrote `sources:` or index lines. This adds the Knowledge Ops API 2.0 the knowledge service and its quality gate build on. The v1 KnowledgeCurator (API 1.1.0) is unchanged. - lifecycle: rule pages parse into frontmatter, head and level-two sections (fence-aware) and render back byte for byte (checked over the whole tree). A rule may end with a `<!-- kb:rule ... -->` footer: status, since, retired_at, reason, evidence, supersedes, superseded_by, protected. - ops: apply_operations() is pure. add (also to a new rules-<topic>.md page plus its index line), edit_same_meaning, replace (retire as superseded and add the successor atomically), retire, purge (retired for a full release; the ID goes to _tombstones.yaml and is reserved forever). It writes updated:, append-only sources:, index lines and tombstones, refuses protected rules without the human path, and refuses a page past the split line. - l1: check_tree() and check_changeset(). The latter enforces the path whitelist and 100644 mode, no new duplicate or reused IDs, no dangling references to retired or purged rules, and no new tree issues. It recomputes every mechanical change and splits the rest into rule and prose blocks with stable block IDs for per-block L2 verdicts. It also reports references outside knowledge/ for companion PRs. - Retired rules stop being served at once: Direct document reads, doc_read, doc_search and briefings drop them; pages without retired rules are served byte for byte as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
The package page was not re-verified when lifecycle, ops and l1 were added, so the strict SPEC freshness gate failed in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1d94d75f62
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| models = data.get("models") | ||
| if models is not None and (not isinstance(models, dict) or not models.get("dir") or not models.get("page")): | ||
| issues.append(Issue("routes_invalid", path, "models needs dir and page")) |
There was a problem hiding this comment.
Validate the model route directory before accepting routes
When _routes.yaml contains a missing or mistyped models.dir, this check accepts it because it validates only that dir and page are nonempty. The activated route later reaches direct_routing.py:490 and calls model_root.iterdir(), raising FileNotFoundError for every review with nonempty intent and breaking Direct routing for that repository. Verify that the directory exists and is a directory before accepting the route.
Useful? React with 👍 / 👎.
| ANY_RULE_HEADING = re.compile( | ||
| r"^#{2,6}\s+(?P<rule>[A-Za-z0-9][A-Za-z0-9-]{1,40})(?=\s|$)", re.MULTILINE) |
There was a problem hiding this comment.
Require rule syntax when collecting nested IDs
The pattern treats the first word of every level-2–6 heading as a rule ID without requiring the rule heading's dash-and-title syntax. In the shipped qwen3-tts/rules.md, the ordinary subheading ### PR #6001 性能证据边界 therefore makes PR a nested ID of Q3TTS-3e; retiring that rule reports PR as retired and produces hundreds of dangling_reference issues across the tree, so the valid lifecycle operation cannot pass L1. Restrict this matcher to actual rule headings.
Useful? React with 👍 / 👎.
| if footer.supersedes: | ||
| source = footers.get(footer.supersedes) | ||
| if source is not None and source[1].superseded_by != rule_id: | ||
| issues.append(Issue("supersede_link_broken", path, | ||
| f"{rule_id} supersedes {footer.supersedes}, which does not name it")) |
There was a problem hiding this comment.
Reject successors whose superseded rule is absent
When an active rule footer declares supersedes=MISSING-ID, source is None and this condition silently accepts the broken link. A hand-authored change can consequently pass L1 with a successor that has no corresponding retired rule, despite the lifecycle contract requiring bidirectional supersession links; emit supersede_link_broken when the source is absent as well as when its backlink is wrong.
Useful? React with 👍 / 👎.
Second PR of the knowledge-base redesign. It adds Knowledge Ops API 2.0, typed changes to rule pages with a rule lifecycle, plus L1, the deterministic half of the knowledge quality gate. The v1
KnowledgeCurator(API 1.1.0) is unchanged.What changes
knowledge_service/lifecycle.py: rule pages as data.##heading, or a###rule heading under a non-rule topic heading. That picks up 102 existing###rules such asSERV-5r.###headings inside a##rule stay with it.<!-- kb:rule ... -->footer (status, since, retired_at, reason, evidence, supersedes, superseded_by, protected). No footer means active.knowledge_service/ops.py: pureapply_operations()covering:add, including to a newrules-<topic>.mdpage, which also gets its_index.mdline;edit_same_meaning;replace: retires the old rule as superseded and adds the successor atomically, keeping the chain intact;retire;purge: only after one full release; the IDs, nested ones included, go into_tombstones.yamland are reserved forever.It writes
updated:and append-onlysources:, refuses protected rules unless on the human path, and refuses any page past the 32 KiB / 500-line split line.knowledge_service/l1.py:check_tree(): footers, supersede links, tombstones,_routes.yaml.check_changeset()enforces:100644;ruleorproseblocks with stableblock_ids for per-block L2 verdicts.knowledge/are reported, for companion PRs.Retired rules stop being served immediately. Direct document reads,
doc_read,doc_searchand briefings drop them. Pages without retired rules are served unchanged.Docs:
doc/knowledge/SCHEMA.mdcovers lifecycle footers and_routes.yaml. SPEC pages added for the three modules.Test Plan
test/test_knowledge_ops_l1.py(29 tests):###rules on the realrules-engine-lifecycle.md;check_spec_freshness,check_doc_links, both knowledge validators.Test Result
test_testing_substrate.py::test_final_scan_kill_works_after_leader_reapedfailed once in a full run (process-timing flake) and passed 3/3 when rerun on its own.check_spec_freshness: 86/86.check_doc_links: OK.check_knowledge_tree: 0 errors.🤖 Generated with Claude Code