feat: living documentation engine (dual-purpose wiki + RAG namespace)#2028
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
Caution Review failedFailed to post review comments WalkthroughAdds a living-documentation subsystem: design docs and navigation; engine modules (models, constants, errors, chunker, indexer, retrieval facade, serializer, slug derivation, writer, factory, DocsService); persistence protocol and SQLite/Postgres repositories with migrations; AppState wiring and ProjectDocsController; agent tools and MCP domain/handlers; observability events; frontend API/types, routes, pages, components, and MSW mocks; ghost-wiring and schema-drift script updates; and comprehensive unit, integration, and conformance tests exercising the full feature. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request implements a living documentation engine that bridges the gap between human-readable project wikis and machine-retrievable agent memory. By storing structured Pydantic models as versioned JSON in the project git workspace and chunking them into a dedicated RAG namespace, the system ensures that project knowledge is both browsable in the dashboard and accessible to agents during task execution. The implementation includes the full stack: persistence protocols, agent tools, MCP handlers, REST controllers, and a web-based viewer. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces the Living Documentation engine, which enables per-project documentation to be used for both human-browsable wikis and agent-retrievable RAG memory. The changes include a new DocsService, git-backed storage, metadata persistence for SQLite and Postgres, and a React-based dashboard. The review highlights a high-severity path traversal risk in slug processing and suggests performance optimizations, such as parallelizing chunk storage with asyncio.TaskGroup and eliminating redundant I/O in the memory facade's error paths. Other feedback includes refining safety bounds in the indexer and ensuring API error codes correctly reflect service availability.
| title: NotBlankStr, | ||
| doc_type: DocType, | ||
| supplied_slug: NotBlankStr | None, |
There was a problem hiding this comment.
The supplied_slug is used directly to construct filesystem paths in read_doc and _read_bytes without validation. This presents a path traversal risk if an agent or MCP client provides a malicious slug (e.g., ../../etc/passwd). Slugs should be validated against a safe alphanumeric/kebab-case regex.
if supplied_slug is not None:
if not re.match(r"^[a-z0-9]+(?:-[a-z0-9]+)*$", supplied_slug):
msg = f"Invalid slug format: {supplied_slug!r}"
raise DocValidationError(msg)
prior = await self._repo.get((project_id, supplied_slug))
return supplied_slug, prior| def _docs_service(state: State) -> DocsService: | ||
| """Resolve the docs service from app state, surfacing 503 if absent.""" | ||
| svc: DocsService | None = state.app_state.docs_service | ||
| if svc is None: | ||
| msg = "Living-documentation engine is not wired in this deployment" | ||
| raise NotFoundError(msg) | ||
| return svc |
There was a problem hiding this comment.
The docstring mentions surfacing a 503 error if the service is absent, but the code raises NotFoundError (404). Per the PR description and general API conventions for optional engines, this should likely be a ServiceUnavailableError (503).
| def _docs_service(state: State) -> DocsService: | |
| """Resolve the docs service from app state, surfacing 503 if absent.""" | |
| svc: DocsService | None = state.app_state.docs_service | |
| if svc is None: | |
| msg = "Living-documentation engine is not wired in this deployment" | |
| raise NotFoundError(msg) | |
| return svc | |
| def _docs_service(state: State) -> DocsService: | |
| """Resolve the docs service from app state, surfacing 503 if absent.""" | |
| svc: DocsService | None = state.app_state.docs_service | |
| if svc is None: | |
| msg = "Living-documentation engine is not wired in this deployment" | |
| from synthorg.core.domain_errors import ServiceUnavailableError | |
| raise ServiceUnavailableError(msg) | |
| return svc |
| logger = get_logger(__name__) | ||
|
|
||
| _INDEX_PAGE_SIZE: int = 100 | ||
| _MAX_DELETE_ITERATIONS: int = 10_000 |
There was a problem hiding this comment.
A safety bound of 10,000 iterations for deleting prior chunks of a single document is excessively high. Given that documents are stored as JSON in git, they are unlikely to have more than a few hundred chunks. A loop of this size performing I/O could lead to significant latency or a potential DoS if the backend misbehaves or is eventually consistent. 100 iterations (allowing 10,000 chunks) is a much safer bound.
| _MAX_DELETE_ITERATIONS: int = 10_000 | |
| _MAX_DELETE_ITERATIONS: int = 100 |
| for chunk in chunks: | ||
| await self._backend.store( | ||
| SYSTEM_DOCS_AGENT_ID, | ||
| _chunk_to_request(chunk), | ||
| ) |
There was a problem hiding this comment.
Storing chunks sequentially with await in a loop is inefficient, especially for documents with many blocks. Consider using asyncio.gather or a TaskGroup to parallelize these backend calls.
try:
async with asyncio.TaskGroup() as tg:
for chunk in chunks:
tg.create_task(
self._backend.store(
SYSTEM_DOCS_AGENT_ID,
_chunk_to_request(chunk),
)
)| except builtins.BaseExceptionGroup as group: | ||
| if group.subgroup(asyncio.CancelledError) is not None: | ||
| raise | ||
| logger.warning( | ||
| DOC_FACADE_FANOUT_FAILED, | ||
| agent_id=agent_id, | ||
| project_id=project_id, | ||
| exceptions=tuple(type(exc).__name__ for exc in group.exceptions), | ||
| error=safe_error_description(group.exceptions[0]) | ||
| if group.exceptions | ||
| else "no exceptions", | ||
| ) | ||
| return await self._backend.retrieve(agent_id, query) |
There was a problem hiding this comment.
In the error path, the facade re-runs the entire retrieval against the agent's own memory. If agent_task has already succeeded, this is redundant I/O. You should check if agent_task is done and successful before deciding to re-fetch.
except builtins.BaseExceptionGroup as group:
if group.subgroup(asyncio.CancelledError) is not None:
raise
logger.warning(
DOC_FACADE_FANOUT_FAILED,
agent_id=agent_id,
project_id=project_id,
exceptions=tuple(type(exc).__name__ for x in group.exceptions),
error=safe_error_description(group.exceptions[0])
if group.exceptions
else "no exceptions",
)
if agent_task.done() and agent_task.exception() is None:
return agent_task.result()
return await self._backend.retrieve(agent_id, query)There was a problem hiding this comment.
Actionable comments posted: 21
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
tests/unit/meta/mcp/test_all_handlers_wired.py (1)
214-221:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate stale pinned tool-count text to
210.Line 214 still documents a 205-tool surface, but Line 221 now asserts 210. Keeping this text aligned avoids future maintenance confusion.
Suggested patch
- """Registry has exactly the documented 205-tool surface. + """Registry has exactly the documented 210-tool surface.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unit/meta/mcp/test_all_handlers_wired.py` around lines 214 - 221, Update the docstring above the registry assertion to match the current pinned count: change the phrase "205-tool surface" to "210-tool surface" in the docstring that precedes the call to build_full_registry() and the assertion on registry.tool_count so the human-readable documentation matches the assert in the test.src/synthorg/persistence/sqlite/backend.py (1)
274-333:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReset
_project_docsin_clear_stateto avoid stale repository handles.Line 279 resets
_project_workspaces, but_project_docsis never cleared. After disconnect/failure paths, this can keep a repo bound to a closed DB connection.Proposed fix
self._projects = None self._project_workspaces = None + self._project_docs = None self._tasks = None🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/synthorg/persistence/sqlite/backend.py` around lines 274 - 333, The _clear_state method fails to reset the _project_docs repository, which can leave a stale repo bound to a closed DB; update the _clear_state function to set self._project_docs = None (similar to the adjacent self._project_workspaces reset) so all repository handles are cleared on disconnect/failure.src/synthorg/persistence/postgres/backend.py (1)
352-410:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReset
_project_docsin_clear_state()to avoid stale repo handles.The new repository is initialized in
__init__and_create_repositories(), but not nulled in_clear_state(). After reconnects, this can leave a stale instance tied to an old pool.Proposed fix
self._projects = None self._project_workspaces = None + self._project_docs = None self._tasks = None🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/synthorg/persistence/postgres/backend.py` around lines 352 - 410, The _clear_state() method is missing a reset for the _project_docs repository, leaving a stale repository tied to the old pool after reconnect; update _clear_state() to set self._project_docs = None (the same pattern used for other repos) so that the repository created in __init__/_create_repositories() is reinitialized on reconnects.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design/living-documentation.md`:
- Line 20: The fenced code block starting with triple backticks around the
directory listing (the block containing "src/synthorg/docs_engine/") is
unlabeled and triggers MD040; fix it by adding a language label (e.g., change
the opening ``` to ```text) so the block becomes a labeled fenced code block
(```text ... ```), leaving the contents unchanged.
In `@scripts/schema_drift_baseline.txt`:
- Around line 112-113: The two baseline entries for columns
project_docs:created_at and project_docs:updated_at currently use the
placeholder justification "auto-generated; replace with audit-cited
justification before commit"; replace those placeholders with full, non-empty
audit-cited justifications following the established pattern (describe SQLite
limitation and how the column is stored/normalized), e.g. explain that SQLite
lacks TIMESTAMPTZ and the column stores ISO-8601 text normalized to UTC at write
time; update both project_docs:created_at and project_docs:updated_at entries to
use similar complete justifications before committing.
In `@src/synthorg/api/controllers/project_docs.py`:
- Around line 42-47: The function that resolves the docs service checks
state.app_state.docs_service and currently raises NotFoundError when svc is
None; change this to raise the service-unavailable error type instead (e.g.,
replace raise NotFoundError(msg) with raise ServiceUnavailableError(msg) or the
project’s ServiceUnavailable/ServiceUnavailableError class) and add/import that
error class at the top of src/synthorg/api/controllers/project_docs.py so the
endpoint returns 503 when the docs engine is unwired.
In `@src/synthorg/core/enums.py`:
- Around line 123-126: The PROJECT_DOC docstring incorrectly states entries are
stored under namespace=f"project:{project_id}"; update the docstring for
PROJECT_DOC to reflect the actual contract: entries are stored in a shared
system docs namespace and are project-scoped via a "project:<id>" tag (not a
separate namespace), and are surfaced to agents via ProjectAwareMemoryFacade
which relies on tag scoping; edit the PROJECT_DOC description to state the
shared system docs namespace + project:<id> tag semantics and remove the
misleading namespace= example.
In `@src/synthorg/docs_engine/indexer.py`:
- Around line 68-83: The exception handlers wrapping calls like await
self._delete_prior(...) and the other block around the re-index step are
converting cancellation and critical system errors into DocIndexError; before
the broad except Exception as exc: add an early re-raise for critical exceptions
(e.g. asyncio.CancelledError, KeyboardInterrupt, SystemExit) and also re-raise
MemoryError and RecursionError per guidelines. In practice, inside both except
blocks (the one around _delete_prior and the later one between lines 84-100)
check if isinstance(exc, (asyncio.CancelledError, KeyboardInterrupt, SystemExit,
MemoryError, RecursionError)) and if so raise, otherwise continue to log via
logger.warning(DOC_INDEX_FAILED, ...) and raise DocIndexError(msg) from exc so
cancellation/system errors propagate unchanged; reference _delete_prior,
DocIndexError, safe_error_description, DOC_INDEX_FAILED, and logger.warning to
locate the handlers.
In `@src/synthorg/docs_engine/service.py`:
- Around line 338-342: The search result mapping currently trusts backend tag
filtering and should explicitly enforce project scoping: after mapping entries
to hits (using _entry_to_hit) filter out any hit whose project_id does not equal
the requested project_id. Concretely, update the comprehension that builds hits
(the tuple assigned to hits using _entry_to_hit(entry, doc_types=doc_types)) to
include an additional guard "and hit.project_id == project_id" (or equivalent)
so only hits matching project_id are kept; apply the same change to the other
analogous mapping block around the _entry_to_hit usage at the second location
(the block referenced at lines ~481-503) so both places enforce project_id
explicitly. Ensure you preserve the existing None-check for _entry_to_hit and
keep behavior identical aside from the added project_id filter.
- Around line 241-244: The code currently only regex-validates the provided
version SHA then runs "git show <sha>:<path>", allowing any reachable commit;
change the validation to also verify the SHA is an ancestor / contained in the
docs branch before calling git show. Locate the places that validate the
"version" parameter and call git show (the two blocks around the current diff
and the second occurrence at lines 454-467), and add a git check such as "git
merge-base --is-ancestor <sha> DOCS_BRANCH_NAME" or "git branch --contains <sha>
--list DOCS_BRANCH_NAME" (or the equivalent libgit2/GitPython call) and
raise/return an error if the SHA is not contained in DOCS_BRANCH_NAME, only then
proceed to run git show <sha>:<path>.
- Around line 419-422: The query limiting existing slugs to the hardcoded
literal 10_000 can cause slug collisions once the doc bucket exceeds that cap;
replace the magic number with a module-level annotated named constant (e.g.,
EXISTING_SLUGS_QUERY_LIMIT: int = <appropriate_value>) and use that constant in
the call to self._repo.query(DocsFilterSpec(...),
limit=EXISTING_SLUGS_QUERY_LIMIT), or better yet remove the single-page cap and
implement paginated fetching (or stream-all) in the code that collects existing
slugs so derive_slug(...) consults the full set; update references around
self._repo.query, DocsFilterSpec, and derive_slug to use the new constant or
pagination logic.
In `@src/synthorg/docs_engine/slug.py`:
- Around line 46-51: The unbounded loop in the slug generation logic (the suffix
loop that builds candidate via _truncate_with_suffix and wraps with NotBlankStr)
needs a defensive ceiling: introduce a MAX_SUFFIX constant (e.g., 10000) and
change the loop to stop when suffix reaches MAX_SUFFIX, returning a clear
failure (raise ValueError or a custom SlugGenerationError) if no free slug is
found; update the loop that uses suffix, candidate, _truncate_with_suffix and
existing_slugs to check suffix < MAX_SUFFIX and raise the error as the fallback.
In `@src/synthorg/docs_engine/writer.py`:
- Around line 133-147: The current except Exception block in the DocCommitError
handling wraps system-level errors like MemoryError and RecursionError; add an
explicit early re-raise for those exceptions (e.g. "except (MemoryError,
RecursionError): raise") before the generic except Exception branch in the
writer code that logs DOC_WRITE_FAILED and raises DocCommitError; keep the
existing logger.warning (using DOC_WRITE_FAILED, project_id, slug, error_type,
safe_error_description(exc)) and then raise DocCommitError(msg) from exc as
before.
In `@src/synthorg/meta/mcp/handlers/docs.py`:
- Around line 208-210: The generic except Exception blocks (e.g., the handler
that calls log_handler_invoke_failed(_TOOL_DOCS_WRITE, exc) and returns
err(exc)) currently swallow system-level errors; update each such catch (the
ones around log_handler_invoke_failed/_TOOL_DOCS_WRITE and the similar blocks at
the other reported locations) to first check and re-raise MemoryError and
RecursionError (e.g., if isinstance(exc, (MemoryError, RecursionError)): raise)
before calling log_handler_invoke_failed and returning err(exc), so system
exceptions propagate while other exceptions are logged and handled as before.
- Around line 264-265: Validate that the incoming offset parameter is an integer
>= 0 instead of silently coercing bad values: read arguments.get(_ARG_OFFSET)
into offset_raw, if not isinstance(offset_raw, int) raise a TypeError (or
ValueError) indicating a bad type, and if offset_raw < 0 raise a ValueError
indicating offset must be non-negative; otherwise assign offset = offset_raw.
Use the variable names offset_raw, offset and the argument key _ARG_OFFSET to
locate and update the logic.
In `@src/synthorg/persistence/protocol.py`:
- Around line 418-421: The class docstring's Attributes list is missing the new
project_docs property; update the class docstring (the attribute inventory in
the protocol) to include an entry for project_docs describing its type
(DocsRepository) and purpose ("Repository for living-documentation metadata
persistence") so the protocol documentation matches the code; locate the class
docstring near the attribute list and add a concise line for project_docs
consistent with the existing attribute formatting.
In `@src/synthorg/persistence/sqlite/schema.sql`:
- Around line 471-475: The current indexes idx_project_docs_updated_at and
idx_project_docs_doc_type can still force a sort for queries using WHERE
project_id = ? ORDER BY updated_at DESC, slug DESC; add a composite
project-scoped recency index on project_docs covering (project_id, updated_at
DESC, slug DESC) (e.g. name it something like idx_project_docs_project_recent)
so the planner can use the index to satisfy both the WHERE and ORDER BY without
an extra sort; update schema.sql to create that composite index alongside the
existing ones.
In `@src/synthorg/tools/docs/search_living_docs.py`:
- Line 66: Replace the direct Pydantic call and use the boundary parser helper:
instead of calling SearchLivingDocsArgs.model_validate(arguments), invoke
parse_typed with the external-dict and the SearchLivingDocsArgs type so the tool
argument ingestion goes through the standardized boundary parsing (use the
parse_typed helper and assign its result to parsed). Ensure you import/retain
the parse_typed symbol and keep the target variable name parsed and the type
SearchLivingDocsArgs referenced.
- Around line 93-106: In execute(), before the broad "except Exception as exc"
block that logs DOC_SEARCH_FAILED and returns a ToolExecutionResult, add logic
to re-raise process-level exceptions (at minimum MemoryError and RecursionError)
so they propagate instead of being converted into tool errors; locate the
try/except around execute()'s main logic and insert explicit "if isinstance(exc,
(MemoryError, RecursionError)): raise" (or separate except clauses) prior to
calling logger.error/safe_error_description and returning the
ToolExecutionResult, leaving the existing logging and error-return behavior for
other Exception types intact.
In `@src/synthorg/tools/docs/write_living_doc.py`:
- Around line 105-118: The current try/except in write_living_doc.py catches all
Exception and thus converts system-level errors into normal tool failures;
before the existing "except Exception as exc" block in the method that returns
ToolExecutionResult, add an explicit passthrough for MemoryError and
RecursionError (e.g., "except (MemoryError, RecursionError): raise") so those
are re-raised, leaving the existing logging (DOC_WRITE_FAILED,
safe_error_description, self._project_id) and ToolExecutionResult handling for
other exceptions intact.
In `@tests/unit/api/fakes.py`:
- Around line 682-684: The fake repository's count method (async def count) is
incorrectly capped by delegating to query(..., limit=10_000); remove the hard
cap by calling self.query without the limit/offset (or otherwise
iterate/paginate until all results are retrieved), or implement a direct count
path that applies the same filter logic to the full dataset and returns len of
all matches; update the count function to use self.query(filter_spec) or
equivalent unbounded logic so it returns an accurate total consistent with the
repository behavior.
In `@tests/unit/core/test_enums.py`:
- Around line 92-94: Update the test_memory_category_has_6_members test to
assert not just the enum length but also that MemoryCategory.PROJECT_DOC is
present and has the expected value; modify the test (function
test_memory_category_has_6_members) to include an explicit assertion like
asserting MemoryCategory.PROJECT_DOC in set(MemoryCategory) or comparing
set(MemoryCategory) == {expected members} so the test verifies the
presence/value of MemoryCategory.PROJECT_DOC in addition to len(MemoryCategory)
== 6.
In `@web/src/api/endpoints/projectDocs.ts`:
- Around line 4-104: This file redefines DTOs (DocType, DocSummary, DocBlock
variants, LivingDocument, DocVersion, DocSearchHit, ListProjectDocsParams,
etc.); remove these local type definitions and import the corresponding
generated types from the "`@/api/types`" barrel instead, then update any local
references to use the imported names (e.g., replace local DocType,
LivingDocument, ListProjectDocsParams usages with the imported DTOs) so the
module only performs transport calls and avoids schema drift.
In `@web/src/pages/ProjectDocsPage.tsx`:
- Around line 34-69: Replace the manual cancelled flag pattern in both useEffect
blocks with an AbortController: create an AbortController instance inside each
effect, pass controller.signal into the listProjectDocs(projectId, signal) and
getProjectDoc(projectId, slug, signal) calls (or update those API wrappers to
accept an optional signal), and call controller.abort() in the effect cleanup;
also guard setDocs and setDocResult against abort by checking for an AbortError
in the catch (skip setting state when aborted) or keep the cancelled boolean but
still abort the fetch in cleanup so in-flight network requests are cancelled
immediately.
---
Outside diff comments:
In `@src/synthorg/persistence/postgres/backend.py`:
- Around line 352-410: The _clear_state() method is missing a reset for the
_project_docs repository, leaving a stale repository tied to the old pool after
reconnect; update _clear_state() to set self._project_docs = None (the same
pattern used for other repos) so that the repository created in
__init__/_create_repositories() is reinitialized on reconnects.
In `@src/synthorg/persistence/sqlite/backend.py`:
- Around line 274-333: The _clear_state method fails to reset the _project_docs
repository, which can leave a stale repo bound to a closed DB; update the
_clear_state function to set self._project_docs = None (similar to the adjacent
self._project_workspaces reset) so all repository handles are cleared on
disconnect/failure.
In `@tests/unit/meta/mcp/test_all_handlers_wired.py`:
- Around line 214-221: Update the docstring above the registry assertion to
match the current pinned count: change the phrase "205-tool surface" to
"210-tool surface" in the docstring that precedes the call to
build_full_registry() and the assertion on registry.tool_count so the
human-readable documentation matches the assert in the test.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 74bf65af-d627-4b74-8dd4-085cbac466a6
📒 Files selected for processing (83)
docs/DESIGN_SPEC.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/living-documentation.mddocs/design/memory.mddocs/design/page-structure.mdscripts/_ghost_wiring_manifest.txtscripts/check_no_ghost_wiring.pyscripts/schema_drift_baseline.txtsrc/synthorg/api/app.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/api/state.pysrc/synthorg/core/enums.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/__init__.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/docs_engine/constants.pysrc/synthorg/docs_engine/errors.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/docs_engine/models.pysrc/synthorg/docs_engine/retrieval_facade.pysrc/synthorg/docs_engine/serializer.pysrc/synthorg/docs_engine/service.pysrc/synthorg/docs_engine/slug.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/observability/events/docs.pysrc/synthorg/observability/events/persistence.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/persistence/postgres/backend.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/persistence/postgres/revisions/20260520000001_project_docs.sqlsrc/synthorg/persistence/postgres/schema.sqlsrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/persistence/sqlite/revisions/20260520000001_project_docs.sqlsrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/tools/docs/__init__.pysrc/synthorg/tools/docs/_args.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/tools/docs/write_living_doc.pytests/conformance/persistence/test_docs_repository.pytests/integration/docs_engine/__init__.pytests/integration/docs_engine/_workspace.pytests/integration/docs_engine/test_living_doc_tools.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/api/conftest.pytests/unit/api/controllers/test_project_docs.pytests/unit/api/fakes.pytests/unit/api/fakes_backend.pytests/unit/core/test_enums.pytests/unit/docs_engine/__init__.pytests/unit/docs_engine/test_chunker.pytests/unit/docs_engine/test_indexer.pytests/unit/docs_engine/test_models.pytests/unit/docs_engine/test_project_doc_category.pytests/unit/docs_engine/test_serializer.pytests/unit/docs_engine/test_slug.pytests/unit/meta/mcp/test_all_handlers_wired.pytests/unit/meta/mcp/test_handlers_docs.pytests/unit/observability/test_events.pytests/unit/persistence/test_protocol.pyweb/src/api/endpoints/projectDocs.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/enum-values.gen.tsweb/src/api/types/error-codes.gen.tsweb/src/api/types/openapi.gen.tsweb/src/mocks/handlers/index.tsweb/src/mocks/handlers/projectDocs.tsweb/src/pages/ProjectDocsPage.tsxweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/pages/project-docs/DocList.tsxweb/src/pages/project-docs/DocViewer.tsxweb/src/router/index.tsxweb/src/router/routes.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
- GitHub Check: Build Backend
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Lighthouse Site
- GitHub Check: CodSpeed Python benchmarks
- GitHub Check: CodSpeed Web benchmarks
- GitHub Check: Test Integration
- GitHub Check: Test Conformance (SQLite)
- GitHub Check: Test E2E
- GitHub Check: Dashboard Test
- GitHub Check: Test Unit
- GitHub Check: Build Preview
- GitHub Check: Analyze (python)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (18)
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Numeric claims in README and public docs must be sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers
Files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
**/*.{md,d2}
📄 CodeRabbit inference engine (CLAUDE.md)
Use D2 for architecture/nested containers, mermaid for flowcharts/sequences/pipelines, markdown tables for tabular data; D2 CLI pinned to v0.7.1 in CI
Files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
web/src/**/*.{js,jsx,ts,tsx,mts}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{js,jsx,ts,tsx,mts}: Always usecreateLoggerfrom@/lib/logger; never bareconsole.warn/console.error/console.debugin application code. Variable name must always belog. Onlylogger.tsitself may use bare console methods. Uselog.debug()(DEV-only, stripped in production),log.warn(),log.error().
Pass dynamic/untrusted values as separate args to logger calls (not interpolated into the message string) so they go throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Error-code constants (MANDATORY): importErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported from the generatedweb/src/api/types/error-codes.gen.ts). Discriminate onErrorCode.<NAME>, never on raw integer literals.
Use@eslint-react/web-api-no-leaked-fetchto detectfetch()in effects withoutAbortControllercleanup
Files:
web/src/api/types/enum-values.gen.tsweb/src/pages/project-docs/DocList.tsxweb/src/router/routes.tsweb/src/pages/project-docs/DocViewer.tsxweb/src/pages/ProjectDocsPage.tsxweb/src/mocks/handlers/projectDocs.tsweb/src/mocks/handlers/index.tsweb/src/router/index.tsxweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/api/types/error-codes.gen.tsweb/src/api/endpoints/projectDocs.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/openapi.gen.ts
web/src/api/types/**/*.gen.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Generated DTO types (MANDATORY): NEVER hand-edit
web/src/api/types/*.gen.ts. Regenerate withuv run python scripts/generate_dto_types_ts.py. Import DTOs via the barrel (import type { AgentConfig } from '@/api/types').
Files:
web/src/api/types/enum-values.gen.tsweb/src/api/types/error-codes.gen.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/openapi.gen.ts
web/src/**/*.{ts,tsx,mts}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx,mts}: Use@typescript-eslint/no-floating-promisesto forbid unawaited promises so async work cannot survive the test that scheduled it and trip the active-handle gate
Use@typescript-eslint/no-misused-promises(withchecksVoidReturn: { attributes: false }) to forbid passing async functions where the callsite ignores the returned promise. React 19asyncevent handlers stay allowed via theattributes: falseexemption.
Files:
web/src/api/types/enum-values.gen.tsweb/src/pages/project-docs/DocList.tsxweb/src/router/routes.tsweb/src/pages/project-docs/DocViewer.tsxweb/src/pages/ProjectDocsPage.tsxweb/src/mocks/handlers/projectDocs.tsweb/src/mocks/handlers/index.tsweb/src/router/index.tsxweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/api/types/error-codes.gen.tsweb/src/api/endpoints/projectDocs.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/openapi.gen.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Reuseweb/src/components/ui/design tokens in Web Dashboard; seeweb/CLAUDE.mdfor details
Web: React 19; see web/CLAUDE.md for web-specific conventions; refer to ~/.claude/rules/common/bash.md for Bash/shell rules
Files:
web/src/api/types/enum-values.gen.tsweb/src/pages/project-docs/DocList.tsxweb/src/router/routes.tsweb/src/pages/project-docs/DocViewer.tsxweb/src/pages/ProjectDocsPage.tsxweb/src/mocks/handlers/projectDocs.tsweb/src/mocks/handlers/index.tsweb/src/router/index.tsxweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/api/types/error-codes.gen.tsweb/src/api/endpoints/projectDocs.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/openapi.gen.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Test markers:@pytest.mark.{unit,integration,e2e,slow}; async auto; 30s timeout global; coverage 80% min
Windows: unit tests use WindowsSelectorEventLoopPolicy; subprocess tests override back
Test doubles: FakeClock + mock_ofT from tests._shared; bare MagicMock at typed boundary blocked by scripts/check_mock_spec.py
Hypothesis: 10 deterministic CI examples; failures are real bugs; fix + add@example(...); HYPOTHESIS_PROFILE=dev or fuzz for property tests
Flaky tests: NEVER skip/xfail; fix fundamentally; use asyncio.Event().wait() not sleep(large)
Files:
tests/integration/docs_engine/_workspace.pytests/unit/api/controllers/test_project_docs.pytests/unit/observability/test_events.pytests/unit/persistence/test_protocol.pytests/unit/meta/mcp/test_all_handlers_wired.pytests/unit/docs_engine/test_project_doc_category.pytests/unit/api/conftest.pytests/unit/core/test_enums.pytests/unit/api/fakes.pytests/unit/docs_engine/test_slug.pytests/unit/docs_engine/test_chunker.pytests/integration/docs_engine/test_living_doc_tools.pytests/unit/api/fakes_backend.pytests/unit/docs_engine/test_serializer.pytests/unit/docs_engine/test_indexer.pytests/unit/docs_engine/test_models.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/meta/mcp/test_handlers_docs.pytests/conformance/persistence/test_docs_repository.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/integration/docs_engine/_workspace.pytests/unit/api/controllers/test_project_docs.pytests/unit/observability/test_events.pytests/unit/persistence/test_protocol.pytests/unit/meta/mcp/test_all_handlers_wired.pytests/unit/docs_engine/test_project_doc_category.pytests/unit/api/conftest.pytests/unit/core/test_enums.pytests/unit/api/fakes.pytests/unit/docs_engine/test_slug.pytests/unit/docs_engine/test_chunker.pytests/integration/docs_engine/test_living_doc_tools.pytests/unit/api/fakes_backend.pytests/unit/docs_engine/test_serializer.pytests/unit/docs_engine/test_indexer.pytests/unit/docs_engine/test_models.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/meta/mcp/test_handlers_docs.pytests/conformance/persistence/test_docs_repository.py
web/src/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{jsx,tsx}: Use@eslint-react/no-leaked-conditional-renderingto catch the{count && <Foo />}bug where0renders verbatim. ForReactNode | undefinedprops use{value != null && value !== false && <jsx>}; for compound truthiness useBoolean(...).
Use@eslint-react/globalsto restrictwindow/document/localStorage/ etc. inside render. Hoist offenders into auseCallbackevent handler, auseEffect, or auseSyncExternalStore-backed hook.
Files:
web/src/pages/project-docs/DocList.tsxweb/src/pages/project-docs/DocViewer.tsxweb/src/pages/ProjectDocsPage.tsxweb/src/router/index.tsxweb/src/pages/project-docs/DocBlockRenderer.tsx
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Configuration precedence: DB > env > code default via SettingsService/ConfigResolver (Cat-1) or env > code default (Cat-2); Cat-3 bootstrap secrets pure env; no os.environ.get outside startup
No hardcoded numeric values; allowlist 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants; enforced by scripts/check_no_magic_numbers.py
Comments must explain WHY only; no reviewer citations, issue back-refs, or migration framing; enforced by check_no_review_origin_in_code.py and check_no_migration_framing.py
Nofrom __future__ import annotations; use PEP 758 style exceptions; type hints on public functions; mypy strict mode; Google-style docstrings
Line length 88; functions < 50 lines; files < 800 lines
Error classes:<Domain><Condition>ErrorfromDomainError, never inherit from Exception/RuntimeError; enforced by check_domain_error_hierarchy.py
Pydantic v2 frozen models withextra="forbid"everywhere;@computed_fieldfor derived fields; useNotBlankStrfor identifiers; gate check_frozen_model_extra_forbid.py
Args models at every system boundary; useparse_typed()for external dict ingestion; enforced by check_boundary_typed.py
Immutability: usemodel_copy(update=...)orcopy.deepcopy(); deepcopy at system boundaries
Async: useasyncio.TaskGroupfor fan-out/fan-in; helpers catchException(re-raise MemoryError/RecursionError)
Clock seam:clock: Clock | None = None; tests injectFakeClock; services own_lifecycle_lock; timed-out stops mark unrestartable
Untrusted content (SEC-1): wrap viawrap_untrusted()fromengine.prompt_safety; useHTMLParseGuardfor HTML
Logging: import fromsynthorg.observability.get_logger; variable namelogger; never useimport loggingorprint()in app code
Event names fromobservability.events.<domain>constants; structured kwargs logging; error paths log WARNING/ERROR before raising; state transitions log INFO after persistence
Secret-log redaction...
Files:
src/synthorg/docs_engine/__init__.pysrc/synthorg/docs_engine/errors.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/persistence/protocol.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/observability/events/docs.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/docs_engine/serializer.pysrc/synthorg/api/app.pysrc/synthorg/observability/events/persistence.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/tools/docs/write_living_doc.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/docs_engine/constants.pysrc/synthorg/docs_engine/slug.pysrc/synthorg/api/state.pysrc/synthorg/tools/docs/__init__.pysrc/synthorg/docs_engine/models.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/tools/docs/_args.pysrc/synthorg/docs_engine/retrieval_facade.pysrc/synthorg/core/enums.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/docs_engine/service.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/persistence/postgres/backend.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/docs_engine/__init__.pysrc/synthorg/docs_engine/errors.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/persistence/protocol.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/observability/events/docs.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/docs_engine/serializer.pysrc/synthorg/api/app.pysrc/synthorg/observability/events/persistence.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/tools/docs/write_living_doc.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/docs_engine/constants.pysrc/synthorg/docs_engine/slug.pysrc/synthorg/api/state.pysrc/synthorg/tools/docs/__init__.pysrc/synthorg/docs_engine/models.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/tools/docs/_args.pysrc/synthorg/docs_engine/retrieval_facade.pysrc/synthorg/core/enums.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/docs_engine/service.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/persistence/postgres/backend.py
web/src/mocks/handlers/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/mocks/handlers/**/*.ts: MSW handlers (MANDATORY):web/src/mocks/handlers/must mirrorweb/src/api/endpoints/*.ts1:1 with a default happy-path handler for every exported endpoint. UseonUnhandledRequest: 'error'in test setup; tests override per-case viaserver.use(...), nevervi.mock('@/api/endpoints/*').
Use typed envelope helpers (successFor,paginatedFor,voidSuccess) to keep MSW handlers in lockstep with endpoint return types
Files:
web/src/mocks/handlers/projectDocs.tsweb/src/mocks/handlers/index.ts
src/synthorg/meta/mcp/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
MCP: 200+ tools under
meta/mcp/domains/; define ToolHandler + args_model; callrequire_admin_guardrails()on admin tools; route through service layers per mcp-handler-contract.md
Files:
src/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/meta/mcp/handlers/docs.py
src/synthorg/meta/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Conversational propose (ChiefOfStaffProposer): POST /meta/chat/propose is opt-in; built by
build_chief_of_staff_proposerwith ENFORCED manifest entry; 503s if provider/persistence/pipeline missing; wrap human content viawrap_untrusted(TAG_TASK_DATA, ...)
Files:
src/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/meta/mcp/handlers/docs.py
src/synthorg/persistence/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/persistence/**/*.py: Onlysrc/synthorg/persistence/may import sqlite/psycopg or emit raw SQL; new repository protocols inherit frompersistence/_generics.pycategories
Repository protocols must inherit from generic categories inpersistence/_generics.pyand stay@runtime_checkable; bespoke methods only under ADR-0001 D7
Repository CRUD:save(entity),get(id),delete(id) -> bool,list_items(...),query(...)returning tuples
Datetime in persistence: useparse_iso_utc/format_iso_utcfrompersistence._shared; reject naive dates; usenormalize_utcfor already-typed
Files:
src/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/persistence/postgres/backend.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
API startup has two phases: construction wires sync services, on_startup wires persistence-dependent services; respect ordering invariants for agent_registry, tunnel_provider, SettingsService, WorkflowExecutionObserver, OntologyService
Files:
src/synthorg/api/controllers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/project_docs.py
web/src/{api/endpoints,stores}/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Cursor pagination (MANDATORY): list endpoints must use opaque cursor-based paging via
PaginationMeta. Stores must keepnextCursor+hasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursor. Display counts must come fromdata.length.
Files:
web/src/api/endpoints/projectDocs.ts
web/src/api/endpoints/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Health / readiness endpoints (MANDATORY):
getLiveness()is always 200 while the process is alive;getReadiness()is 200 healthy / 503 unavailable (binary'ok' | 'unavailable'outcome, no tri-state). Any new caller must handle the 503 path explicitly.
Files:
web/src/api/endpoints/projectDocs.ts
tests/conformance/persistence/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Dual-backend conformance: tests/conformance/persistence/ consumes backend fixture (SQLite + Postgres); enforced by check_dual_backend_test_parity.py
Files:
tests/conformance/persistence/test_docs_repository.py
🧠 Learnings (5)
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, do not require adding `<!--RS:...-->` “Doc Numeric Claims (MANDATORY)” numeric macros for Python version numbers mentioned in documentation prose (e.g., “Python 3.14”, “Python 3.15”). The `scripts/check_doc_numeric_macros.py` gate only applies to `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`, and it only flags digits adjacent to specific stat nouns (tests/providers/agents/stars/releases), not language version mentions like “Python 3.14”.
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo, account for the CI gate `check_doc_numeric_macros.py`: it skips fenced code blocks entirely, and it only flags digits that are adjacent to these stat nouns: `tests`, `providers`, `agents`, `stars`, `releases`. Therefore, numeric examples such as CLI flag values (e.g., `--num-workers=4` in fenced bash blocks) and prose version numbers (e.g., `3.14`/`3.15`) are not expected to trigger this check; prioritize changes only when digits appear next to one of the listed nouns (e.g., “5 tests”, “10 stars”, etc.).
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing markdown files for the "Doc Numeric Claims (MANDATORY)" RS-marker rule, only require/flag missing RS markers in the files that are actually in-scope for the rule. The scope is enforced via an identical _SCOPED_FILES allowlist in scripts/check_doc_numeric_macros.py and scripts/inject_runtime_stats.py, and currently includes: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. For any other markdown files (e.g., docs/getting_started.md, docs/guides/*), missing RS markers for numeric claims are no-ops and should NOT be flagged.
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo against the `check_doc_numeric_macros.py` gate, account for its documented behavior: it skips fenced code blocks entirely, and it only flags digits that are adjacent to specific stat nouns (`tests`, `providers`, `agents`, `stars`, `releases`). As a result, CLI-style numbers (e.g., `--num-workers=4`) inside fenced bash code blocks should never be treated as violations of this gate; only non-fenced text needs checking, and only around those specific nouns.
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
tests/integration/docs_engine/_workspace.pytests/unit/api/controllers/test_project_docs.pytests/unit/observability/test_events.pytests/unit/persistence/test_protocol.pysrc/synthorg/docs_engine/__init__.pytests/unit/meta/mcp/test_all_handlers_wired.pysrc/synthorg/docs_engine/errors.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/persistence/protocol.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/observability/events/docs.pyscripts/check_no_ghost_wiring.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/__init__.pytests/unit/docs_engine/test_project_doc_category.pysrc/synthorg/docs_engine/serializer.pytests/unit/api/conftest.pytests/unit/core/test_enums.pytests/unit/api/fakes.pysrc/synthorg/api/app.pysrc/synthorg/observability/events/persistence.pytests/unit/docs_engine/test_slug.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/meta/mcp/domains/_docs_args.pytests/unit/docs_engine/test_chunker.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/tools/docs/write_living_doc.pytests/integration/docs_engine/test_living_doc_tools.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/docs_engine/constants.pysrc/synthorg/docs_engine/slug.pytests/unit/api/fakes_backend.pysrc/synthorg/api/state.pysrc/synthorg/tools/docs/__init__.pytests/unit/docs_engine/test_serializer.pysrc/synthorg/docs_engine/models.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/tools/docs/_args.pytests/unit/docs_engine/test_indexer.pysrc/synthorg/docs_engine/retrieval_facade.pysrc/synthorg/core/enums.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/persistence/sqlite/_backend_accessors.pytests/unit/docs_engine/test_models.pysrc/synthorg/docs_engine/chunker.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/meta/mcp/test_handlers_docs.pysrc/synthorg/docs_engine/service.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/persistence/postgres/backend.pytests/conformance/persistence/test_docs_repository.py
🪛 LanguageTool
scripts/_ghost_wiring_manifest.txt
[uncategorized] ~61-~61: Do not mix variants of the same word (‘serialize’ and ‘serialise’) within a single text.
Context: ..._engine/factory.py::build_docs_service; serializes doc + commits on synthorg/docs via the ...
(EN_WORD_COHERENCY)
🪛 markdownlint-cli2 (0.22.1)
docs/design/living-documentation.md
[warning] 20-20: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (66)
docs/DESIGN_SPEC.md (1)
30-30: LGTM!web/src/api/types/enum-values.gen.ts (2)
274-279: LGTM!
434-434: LGTM!tests/integration/docs_engine/_workspace.py (1)
1-27: LGTM!tests/unit/api/controllers/test_project_docs.py (1)
1-139: LGTM!docs/design/memory.md (2)
28-28: LGTM!
93-93: LGTM!docs/decisions/0001-repository-protocol-consolidation.md (1)
181-181: LGTM!src/synthorg/persistence/postgres/revisions/20260520000001_project_docs.sql (1)
1-35: LGTM!src/synthorg/persistence/postgres/schema.sql (1)
469-494: LGTM!tests/unit/observability/test_events.py (1)
242-242: LGTM!web/src/pages/project-docs/DocList.tsx (1)
1-108: LGTM!web/src/router/routes.ts (1)
32-33: LGTM!tests/unit/persistence/test_protocol.py (1)
1029-1034: LGTM!web/src/pages/project-docs/DocViewer.tsx (1)
1-59: LGTM!web/src/pages/ProjectDocsPage.tsx (1)
79-89: LGTM!src/synthorg/docs_engine/__init__.py (1)
1-69: LGTM!web/src/mocks/handlers/projectDocs.ts (1)
1-127: LGTM!src/synthorg/docs_engine/errors.py (1)
1-73: LGTM!src/synthorg/meta/mcp/domains/__init__.py (1)
13-13: LGTM!Also applies to: 45-45
web/src/mocks/handlers/index.ts (1)
89-89: LGTM!Also applies to: 141-141, 190-190
src/synthorg/meta/mcp/handlers/__init__.py (1)
16-16: LGTM!Also applies to: 52-52
src/synthorg/observability/events/docs.py (1)
1-26: LGTM!scripts/check_no_ghost_wiring.py (1)
65-81: LGTM!web/src/router/index.tsx (1)
34-34: LGTM!Also applies to: 159-160
src/synthorg/persistence/sqlite/backend.py (1)
68-68: LGTM!Also applies to: 210-210, 439-442
scripts/_ghost_wiring_manifest.txt (1)
57-65: LGTM!docs/design/page-structure.md (1)
97-98: LGTM!Also applies to: 108-109, 384-385
src/synthorg/api/controllers/__init__.py (1)
67-67: LGTM!Also applies to: 116-116, 246-246
tests/unit/docs_engine/test_project_doc_category.py (1)
1-99: LGTM!src/synthorg/docs_engine/serializer.py (1)
28-50: LGTM!Also applies to: 52-98
web/src/pages/project-docs/DocBlockRenderer.tsx (1)
15-92: LGTM!tests/unit/api/conftest.py (1)
735-742: LGTM!src/synthorg/api/app.py (1)
1147-1196: LGTM!src/synthorg/observability/events/persistence.py (1)
323-344: LGTM!tests/unit/docs_engine/test_slug.py (1)
1-40: LGTM!web/src/api/types/error-codes.gen.ts (1)
26-26: LGTM!Also applies to: 45-45, 63-63, 100-101
src/synthorg/docs_engine/tool_factory.py (1)
1-90: LGTM!src/synthorg/persistence/docs_protocol.py (1)
1-167: LGTM!src/synthorg/core/error_taxonomy.py (1)
74-75: LGTM!Also applies to: 95-96, 115-116, 160-161
src/synthorg/docs_engine/factory.py (1)
29-87: LGTM!src/synthorg/meta/mcp/domains/_docs_args.py (1)
12-80: LGTM!tests/unit/docs_engine/test_chunker.py (1)
44-139: LGTM!tests/integration/docs_engine/test_living_doc_tools.py (1)
69-178: LGTM!src/synthorg/meta/mcp/domains/docs.py (1)
35-159: LGTM!src/synthorg/docs_engine/constants.py (1)
1-86: LGTM!tests/unit/api/fakes_backend.py (1)
36-36: LGTM!Also applies to: 600-600, 719-722
src/synthorg/api/state.py (1)
150-154: LGTM!Also applies to: 232-234, 282-282, 482-488, 1024-1071
src/synthorg/tools/docs/__init__.py (1)
1-13: LGTM!tests/unit/docs_engine/test_serializer.py (1)
1-110: LGTM!web/src/api/types/dtos.gen.ts (1)
68-68: LGTM!Also applies to: 154-154, 169-169, 210-210, 217-219, 260-260, 269-269, 271-271, 287-287, 309-309, 370-370
src/synthorg/docs_engine/models.py (1)
1-389: LGTM!src/synthorg/persistence/postgres/docs_repo.py (1)
41-348: LGTM!src/synthorg/tools/docs/_args.py (1)
32-195: LGTM!tests/unit/docs_engine/test_indexer.py (1)
24-146: LGTM!src/synthorg/docs_engine/retrieval_facade.py (1)
52-141: LGTM!src/synthorg/core/enums.py (1)
137-150: LGTM!src/synthorg/persistence/sqlite/docs_repo.py (1)
47-341: LGTM!src/synthorg/api/controllers/project_docs.py (1)
88-187: LGTM!src/synthorg/persistence/sqlite/_backend_accessors.py (1)
58-59: LGTM!Also applies to: 148-149, 314-318
tests/unit/docs_engine/test_models.py (1)
1-140: LGTM!src/synthorg/docs_engine/chunker.py (1)
1-226: LGTM!tests/integration/docs_engine/test_service_round_trip.py (1)
1-338: LGTM!tests/unit/meta/mcp/test_handlers_docs.py (1)
1-224: LGTM!web/src/api/types/openapi.gen.ts (1)
2675-2742: LGTM!Also applies to: 5217-5224, 5593-5608, 6320-6331, 6577-6590, 7405-7418, 7529-7584, 7776-7776, 8319-8333, 8466-8479, 8493-8527, 8803-8810, 8937-8952, 9337-9351, 10287-10298, 19070-19203
tests/conformance/persistence/test_docs_repository.py (1)
1-205: LGTM!
| try: | ||
| await self._delete_prior(project_id=project_id, slug=slug) | ||
| except Exception as exc: | ||
| msg = ( | ||
| f"Failed to clear prior chunks for {project_id!r}/{slug!r} " | ||
| f"before re-index" | ||
| ) | ||
| logger.warning( | ||
| DOC_INDEX_FAILED, | ||
| project_id=project_id, | ||
| slug=slug, | ||
| phase="delete_prior", | ||
| error_type=type(exc).__name__, | ||
| error=safe_error_description(exc), | ||
| ) | ||
| raise DocIndexError(msg) from exc |
There was a problem hiding this comment.
Preserve cancellation and system-error propagation in both failure wrappers.
Line 70 and Line 90 currently convert cancellation/system exceptions into DocIndexError. Add explicit pass-through before the broad handler.
Proposed fix
+import asyncio
+import builtins
@@
try:
await self._delete_prior(project_id=project_id, slug=slug)
+ except asyncio.CancelledError:
+ raise
+ except builtins.MemoryError, RecursionError:
+ raise
except Exception as exc:
@@
try:
for chunk in chunks:
await self._backend.store(
SYSTEM_DOCS_AGENT_ID,
_chunk_to_request(chunk),
)
+ except asyncio.CancelledError:
+ raise
+ except builtins.MemoryError, RecursionError:
+ raise
except Exception as exc:Also applies to: 84-100
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/synthorg/docs_engine/indexer.py` around lines 68 - 83, The exception
handlers wrapping calls like await self._delete_prior(...) and the other block
around the re-index step are converting cancellation and critical system errors
into DocIndexError; before the broad except Exception as exc: add an early
re-raise for critical exceptions (e.g. asyncio.CancelledError,
KeyboardInterrupt, SystemExit) and also re-raise MemoryError and RecursionError
per guidelines. In practice, inside both except blocks (the one around
_delete_prior and the later one between lines 84-100) check if isinstance(exc,
(asyncio.CancelledError, KeyboardInterrupt, SystemExit, MemoryError,
RecursionError)) and if so raise, otherwise continue to log via
logger.warning(DOC_INDEX_FAILED, ...) and raise DocIndexError(msg) from exc so
cancellation/system errors propagate unchanged; reference _delete_prior,
DocIndexError, safe_error_description, DOC_INDEX_FAILED, and logger.warning to
locate the handlers.
417aa42 to
00cb046
Compare
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design/memory.md`:
- Line 93: The docs now list PROJECT_DOC in the MemoryCategory enum but the
"Memory Types" table omits it; update the Memory Types section to include
PROJECT_DOC (referencing the MemoryCategory symbol) with a brief description of
what PROJECT_DOC represents (e.g., project-level documentation or repo artifacts
used as long-lived project memory), or alternatively remove PROJECT_DOC from
MemoryCategory if it shouldn’t exist—ensure the Memory Types table and the
MemoryCategory enum are consistent.
In `@scripts/check_schema_drift.py`:
- Around line 292-317: In _do_update_baseline, trim and validate the incoming
justification: if justification is not None then set justification =
justification.strip(); if that yields an empty string raise ValueError (or
otherwise abort) instead of using it to backfill placeholders; only use a
non-empty trimmed justification to set default_reason and to backfill entries
that equal _PLACEHOLDER_BASELINE_REASON (leave justification as None for all
other behavior). Ensure references: _do_update_baseline, justification,
default_reason, _PLACEHOLDER_BASELINE_REASON, and load_baseline_with_reasons.
In `@src/synthorg/docs_engine/chunker.py`:
- Around line 89-103: The prose accumulation loop only stops when
_token_count(_prose_run_text(prose_run)) >= self._target_tokens, so the hard cap
self._max_tokens is ignored; modify the condition that checks run length in the
loop that processes ProseBlock instances (the prose_run append path) to enforce
the smaller of self._target_tokens and self._max_tokens (or explicitly check >=
self._max_tokens as well) before calling self._make_prose_chunk and resetting
prose_run; ensure the change uses the existing helpers _token_count and
_prose_run_text and preserves chunk indexing (len(chunks)) and base_tags
behavior.
In `@src/synthorg/docs_engine/service.py`:
- Around line 544-548: The _extract_tag function can raise when a tag equals the
prefix (empty suffix) or when converting the suffix to NotBlankStr fails; update
_extract_tag (used with MemoryEntry.metadata.tags) to ensure the tag has a
non-empty suffix (len(tag) > len(prefix)) before slicing and to catch/validate
conversion to NotBlankStr (e.g., try/except or validation) returning None on
invalid/malformed tags so a single bad MemoryEntry cannot break the search.
- Around line 433-437: When a supplied_slug is provided the code must enforce
update semantics instead of falling through to create: in the block that calls
_validate_slug(supplied_slug) and prior = await self._repo.get((project_id,
supplied_slug)), if prior is None raise an appropriate NotFound/InvalidOperation
error (do not return and proceed to create); only return supplied_slug, prior
when prior exists. Keep the rest of the path
(DocsFilterSpec(project_id=project_id, doc_type=doc_type)) for the
create-without-slug flow unchanged.
In `@src/synthorg/meta/mcp/handlers/docs.py`:
- Around line 282-283: Replace the hardcoded pagination literals with the
module-level named defaults: use DOCS_LIST_DEFAULT_LIMIT (or
DOCS_SEARCH_DEFAULT_LIMIT / DOCS_HISTORY_DEFAULT_LIMIT where appropriate)
instead of 50 in calls to _parse_positive_int and keep using
DOCS_HISTORY_DEFAULT_LIMIT for history endpoints; update the three call sites
referencing _ARG_LIMIT/_ARG_OFFSET (including the occurrences around
_parse_positive_int/_parse_nonneg_int at the shown locations) to pass the
appropriate DOCS_*_DEFAULT_LIMIT constants as the default parameter so there are
no inline magic numbers.
- Around line 199-204: The optional string arguments slug, version, and tag are
currently coerced to None when they are non-string values, which masks client
bugs; instead, for each of those fields (see the slug assignment using
NotBlankStr on slug_raw, and the similar blocks for version and tag) validate
that if the incoming value is not None it must be a str (and for non-blank
strings wrap with NotBlankStr), and if it is a non-null non-str call
invalid_argument(...) with a clear message; update the slug, version, and tag
assignment blocks to raise invalid_argument for bad types and only return None
for None or blank strings, preserving the NotBlankStr wrapping for valid
non-blank strings.
In `@src/synthorg/persistence/sqlite/docs_repo.py`:
- Around line 271-293: The count method is fragile because it mutates the SELECT
text returned by _build_query_sql; refactor _build_query_sql to return the
composable WHERE (and any FROM/joins) plus params (e.g. return (where_sql,
params) or a small QueryParts object) so both query() and count() can build
their own SELECT heads; update count to compose "SELECT COUNT(*) AS n" + the
returned WHERE/FROM fragment and pass the same params, and update any callers of
_build_query_sql (e.g. the query method) to use the new return shape to preserve
parameter ordering and avoid brittle string replaces.
- Around line 321-340: _tag-containing quote characters break LIKE matching
because JSON stores tags with escaped quotes; in _build_query_sql, when building
the tags LIKE pattern (currently using f'%"{_escape_like(filter_spec.tag)}"%'),
also escape literal quote characters in the tag before interpolation (or use a
JSON-encoding of the tag) so the pattern matches the stored JSON representation;
update the code that calls _escape_like (and/or add a small helper) to replace
'"' with '\\"' (or use json.dumps(filter_spec.tag) and strip surrounding quotes)
before wrapping with %...% and keep the ESCAPE '\\' usage intact so tags like
say "hi" will match the stored ["say \"hi\""].
In `@src/synthorg/tools/docs/write_living_doc.py`:
- Line 130: Replace the hardcoded "12" with a module-level annotated constant
(e.g., SHORT_SHA_LENGTH: int = 12) declared near the top of
src/synthorg/tools/docs/write_living_doc.py, then use that constant where the
commit SHA is sliced (replace metadata.head_commit_sha[:12] with
metadata.head_commit_sha[:SHORT_SHA_LENGTH]); ensure the constant is annotated
as int and named in ALL_CAPS to match repo conventions so the
check_no_magic_numbers.py script accepts it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 17dab383-ee26-4898-9770-6ae18724c184
📒 Files selected for processing (87)
data/runtime_stats.yamldocs/DESIGN_SPEC.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/living-documentation.mddocs/design/memory.mddocs/design/page-structure.mdscripts/_ghost_wiring_manifest.txtscripts/check_no_ghost_wiring.pyscripts/check_schema_drift.pyscripts/schema_drift_baseline.txtsrc/synthorg/api/app.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/api/state.pysrc/synthorg/core/enums.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/__init__.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/docs_engine/constants.pysrc/synthorg/docs_engine/errors.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/docs_engine/models.pysrc/synthorg/docs_engine/retrieval_facade.pysrc/synthorg/docs_engine/serializer.pysrc/synthorg/docs_engine/service.pysrc/synthorg/docs_engine/slug.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/observability/events/docs.pysrc/synthorg/observability/events/persistence.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/persistence/postgres/backend.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/persistence/postgres/revisions/20260520000001_project_docs.sqlsrc/synthorg/persistence/postgres/schema.sqlsrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/persistence/sqlite/revisions/20260520000001_project_docs.sqlsrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/tools/docs/__init__.pysrc/synthorg/tools/docs/_args.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/tools/docs/write_living_doc.pytests/conformance/persistence/test_docs_repository.pytests/integration/docs_engine/__init__.pytests/integration/docs_engine/_workspace.pytests/integration/docs_engine/test_living_doc_tools.pytests/integration/docs_engine/test_service_round_trip.pytests/integration/mcp/test_tool_surface.pytests/unit/api/conftest.pytests/unit/api/controllers/test_project_docs.pytests/unit/api/fakes.pytests/unit/api/fakes_backend.pytests/unit/core/test_enums.pytests/unit/docs_engine/__init__.pytests/unit/docs_engine/test_chunker.pytests/unit/docs_engine/test_indexer.pytests/unit/docs_engine/test_models.pytests/unit/docs_engine/test_project_doc_category.pytests/unit/docs_engine/test_serializer.pytests/unit/docs_engine/test_slug.pytests/unit/meta/mcp/test_all_handlers_wired.pytests/unit/meta/mcp/test_handlers_docs.pytests/unit/observability/test_events.pytests/unit/persistence/test_protocol.pytests/unit/scripts/test_check_schema_drift.pyweb/src/api/endpoints/projectDocs.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/enum-values.gen.tsweb/src/api/types/error-codes.gen.tsweb/src/api/types/openapi.gen.tsweb/src/mocks/handlers/index.tsweb/src/mocks/handlers/projectDocs.tsweb/src/pages/ProjectDocsPage.tsxweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/pages/project-docs/DocList.tsxweb/src/pages/project-docs/DocViewer.tsxweb/src/router/index.tsxweb/src/router/routes.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
- GitHub Check: Deploy Preview
- GitHub Check: Build Backend
- GitHub Check: Test E2E
- GitHub Check: Test Integration
- GitHub Check: Test Conformance (SQLite)
- GitHub Check: Test Unit
- GitHub Check: Dashboard Test
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Lighthouse Site
- GitHub Check: Build Web Assets (melange)
- GitHub Check: CodSpeed Python benchmarks
- GitHub Check: CodSpeed Web benchmarks
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (18)
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Test markers:@pytest.mark.{unit,integration,e2e,slow}; async auto; 30s timeout global; coverage 80% min
Windows: unit tests use WindowsSelectorEventLoopPolicy; subprocess tests override back
Test doubles: FakeClock + mock_ofT from tests._shared; bare MagicMock at typed boundary blocked by scripts/check_mock_spec.py
Hypothesis: 10 deterministic CI examples; failures are real bugs; fix + add@example(...); HYPOTHESIS_PROFILE=dev or fuzz for property tests
Flaky tests: NEVER skip/xfail; fix fundamentally; use asyncio.Event().wait() not sleep(large)
Files:
tests/unit/meta/mcp/test_all_handlers_wired.pytests/unit/observability/test_events.pytests/unit/docs_engine/test_slug.pytests/unit/core/test_enums.pytests/integration/docs_engine/_workspace.pytests/unit/api/conftest.pytests/unit/api/fakes.pytests/integration/mcp/test_tool_surface.pytests/unit/docs_engine/test_models.pytests/unit/api/fakes_backend.pytests/integration/docs_engine/test_living_doc_tools.pytests/unit/docs_engine/test_project_doc_category.pytests/unit/api/controllers/test_project_docs.pytests/unit/scripts/test_check_schema_drift.pytests/unit/docs_engine/test_indexer.pytests/unit/docs_engine/test_serializer.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/docs_engine/test_chunker.pytests/conformance/persistence/test_docs_repository.pytests/unit/meta/mcp/test_handlers_docs.pytests/unit/persistence/test_protocol.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/meta/mcp/test_all_handlers_wired.pytests/unit/observability/test_events.pytests/unit/docs_engine/test_slug.pytests/unit/core/test_enums.pytests/integration/docs_engine/_workspace.pytests/unit/api/conftest.pytests/unit/api/fakes.pytests/integration/mcp/test_tool_surface.pytests/unit/docs_engine/test_models.pytests/unit/api/fakes_backend.pytests/integration/docs_engine/test_living_doc_tools.pytests/unit/docs_engine/test_project_doc_category.pytests/unit/api/controllers/test_project_docs.pytests/unit/scripts/test_check_schema_drift.pytests/unit/docs_engine/test_indexer.pytests/unit/docs_engine/test_serializer.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/docs_engine/test_chunker.pytests/conformance/persistence/test_docs_repository.pytests/unit/meta/mcp/test_handlers_docs.pytests/unit/persistence/test_protocol.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Configuration precedence: DB > env > code default via SettingsService/ConfigResolver (Cat-1) or env > code default (Cat-2); Cat-3 bootstrap secrets pure env; no os.environ.get outside startup
No hardcoded numeric values; allowlist 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants; enforced by scripts/check_no_magic_numbers.py
Comments must explain WHY only; no reviewer citations, issue back-refs, or migration framing; enforced by check_no_review_origin_in_code.py and check_no_migration_framing.py
Nofrom __future__ import annotations; use PEP 758 style exceptions; type hints on public functions; mypy strict mode; Google-style docstrings
Line length 88; functions < 50 lines; files < 800 lines
Error classes:<Domain><Condition>ErrorfromDomainError, never inherit from Exception/RuntimeError; enforced by check_domain_error_hierarchy.py
Pydantic v2 frozen models withextra="forbid"everywhere;@computed_fieldfor derived fields; useNotBlankStrfor identifiers; gate check_frozen_model_extra_forbid.py
Args models at every system boundary; useparse_typed()for external dict ingestion; enforced by check_boundary_typed.py
Immutability: usemodel_copy(update=...)orcopy.deepcopy(); deepcopy at system boundaries
Async: useasyncio.TaskGroupfor fan-out/fan-in; helpers catchException(re-raise MemoryError/RecursionError)
Clock seam:clock: Clock | None = None; tests injectFakeClock; services own_lifecycle_lock; timed-out stops mark unrestartable
Untrusted content (SEC-1): wrap viawrap_untrusted()fromengine.prompt_safety; useHTMLParseGuardfor HTML
Logging: import fromsynthorg.observability.get_logger; variable namelogger; never useimport loggingorprint()in app code
Event names fromobservability.events.<domain>constants; structured kwargs logging; error paths log WARNING/ERROR before raising; state transitions log INFO after persistence
Secret-log redaction...
Files:
src/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/tools/docs/__init__.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/observability/events/persistence.pysrc/synthorg/docs_engine/serializer.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/docs_engine/errors.pysrc/synthorg/core/enums.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/tools/docs/write_living_doc.pysrc/synthorg/docs_engine/slug.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/persistence/protocol.pysrc/synthorg/docs_engine/retrieval_facade.pysrc/synthorg/docs_engine/__init__.pysrc/synthorg/observability/events/docs.pysrc/synthorg/persistence/postgres/backend.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/docs_engine/constants.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/docs_engine/models.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/api/app.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/docs_engine/service.pysrc/synthorg/tools/docs/_args.py
src/synthorg/meta/mcp/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
MCP: 200+ tools under
meta/mcp/domains/; define ToolHandler + args_model; callrequire_admin_guardrails()on admin tools; route through service layers per mcp-handler-contract.md
Files:
src/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/meta/mcp/domains/_docs_args.py
src/synthorg/meta/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Conversational propose (ChiefOfStaffProposer): POST /meta/chat/propose is opt-in; built by
build_chief_of_staff_proposerwith ENFORCED manifest entry; 503s if provider/persistence/pipeline missing; wrap human content viawrap_untrusted(TAG_TASK_DATA, ...)
Files:
src/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/meta/mcp/domains/_docs_args.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/tools/docs/__init__.pysrc/synthorg/meta/mcp/domains/__init__.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/observability/events/persistence.pysrc/synthorg/docs_engine/serializer.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/docs_engine/errors.pysrc/synthorg/core/enums.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/tools/docs/write_living_doc.pysrc/synthorg/docs_engine/slug.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/persistence/protocol.pysrc/synthorg/docs_engine/retrieval_facade.pysrc/synthorg/docs_engine/__init__.pysrc/synthorg/observability/events/docs.pysrc/synthorg/persistence/postgres/backend.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/docs_engine/constants.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/meta/mcp/domains/_docs_args.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/docs_engine/models.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/api/app.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/docs_engine/service.pysrc/synthorg/tools/docs/_args.py
web/src/**/*.{js,jsx,ts,tsx,mts}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{js,jsx,ts,tsx,mts}: Always usecreateLoggerfrom@/lib/logger; never bareconsole.warn/console.error/console.debugin application code. Variable name must always belog. Onlylogger.tsitself may use bare console methods. Uselog.debug()(DEV-only, stripped in production),log.warn(),log.error().
Pass dynamic/untrusted values as separate args to logger calls (not interpolated into the message string) so they go throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Error-code constants (MANDATORY): importErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported from the generatedweb/src/api/types/error-codes.gen.ts). Discriminate onErrorCode.<NAME>, never on raw integer literals.
Use@eslint-react/web-api-no-leaked-fetchto detectfetch()in effects withoutAbortControllercleanup
Files:
web/src/router/routes.tsweb/src/mocks/handlers/index.tsweb/src/api/types/enum-values.gen.tsweb/src/pages/project-docs/DocViewer.tsxweb/src/mocks/handlers/projectDocs.tsweb/src/api/types/error-codes.gen.tsweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/api/endpoints/projectDocs.tsweb/src/pages/ProjectDocsPage.tsxweb/src/api/types/dtos.gen.tsweb/src/pages/project-docs/DocList.tsxweb/src/router/index.tsxweb/src/api/types/openapi.gen.ts
web/src/**/*.{ts,tsx,mts}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx,mts}: Use@typescript-eslint/no-floating-promisesto forbid unawaited promises so async work cannot survive the test that scheduled it and trip the active-handle gate
Use@typescript-eslint/no-misused-promises(withchecksVoidReturn: { attributes: false }) to forbid passing async functions where the callsite ignores the returned promise. React 19asyncevent handlers stay allowed via theattributes: falseexemption.
Files:
web/src/router/routes.tsweb/src/mocks/handlers/index.tsweb/src/api/types/enum-values.gen.tsweb/src/pages/project-docs/DocViewer.tsxweb/src/mocks/handlers/projectDocs.tsweb/src/api/types/error-codes.gen.tsweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/api/endpoints/projectDocs.tsweb/src/pages/ProjectDocsPage.tsxweb/src/api/types/dtos.gen.tsweb/src/pages/project-docs/DocList.tsxweb/src/router/index.tsxweb/src/api/types/openapi.gen.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Reuseweb/src/components/ui/design tokens in Web Dashboard; seeweb/CLAUDE.mdfor details
Web: React 19; see web/CLAUDE.md for web-specific conventions; refer to ~/.claude/rules/common/bash.md for Bash/shell rules
Files:
web/src/router/routes.tsweb/src/mocks/handlers/index.tsweb/src/api/types/enum-values.gen.tsweb/src/pages/project-docs/DocViewer.tsxweb/src/mocks/handlers/projectDocs.tsweb/src/api/types/error-codes.gen.tsweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/api/endpoints/projectDocs.tsweb/src/pages/ProjectDocsPage.tsxweb/src/api/types/dtos.gen.tsweb/src/pages/project-docs/DocList.tsxweb/src/router/index.tsxweb/src/api/types/openapi.gen.ts
web/src/mocks/handlers/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/mocks/handlers/**/*.ts: MSW handlers (MANDATORY):web/src/mocks/handlers/must mirrorweb/src/api/endpoints/*.ts1:1 with a default happy-path handler for every exported endpoint. UseonUnhandledRequest: 'error'in test setup; tests override per-case viaserver.use(...), nevervi.mock('@/api/endpoints/*').
Use typed envelope helpers (successFor,paginatedFor,voidSuccess) to keep MSW handlers in lockstep with endpoint return types
Files:
web/src/mocks/handlers/index.tsweb/src/mocks/handlers/projectDocs.ts
web/src/api/types/**/*.gen.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Generated DTO types (MANDATORY): NEVER hand-edit
web/src/api/types/*.gen.ts. Regenerate withuv run python scripts/generate_dto_types_ts.py. Import DTOs via the barrel (import type { AgentConfig } from '@/api/types').
Files:
web/src/api/types/enum-values.gen.tsweb/src/api/types/error-codes.gen.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/openapi.gen.ts
web/src/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{jsx,tsx}: Use@eslint-react/no-leaked-conditional-renderingto catch the{count && <Foo />}bug where0renders verbatim. ForReactNode | undefinedprops use{value != null && value !== false && <jsx>}; for compound truthiness useBoolean(...).
Use@eslint-react/globalsto restrictwindow/document/localStorage/ etc. inside render. Hoist offenders into auseCallbackevent handler, auseEffect, or auseSyncExternalStore-backed hook.
Files:
web/src/pages/project-docs/DocViewer.tsxweb/src/pages/project-docs/DocBlockRenderer.tsxweb/src/pages/ProjectDocsPage.tsxweb/src/pages/project-docs/DocList.tsxweb/src/router/index.tsx
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Numeric claims in README and public docs must be sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers
Files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
**/*.{md,d2}
📄 CodeRabbit inference engine (CLAUDE.md)
Use D2 for architecture/nested containers, mermaid for flowcharts/sequences/pipelines, markdown tables for tabular data; D2 CLI pinned to v0.7.1 in CI
Files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
API startup has two phases: construction wires sync services, on_startup wires persistence-dependent services; respect ordering invariants for agent_registry, tunnel_provider, SettingsService, WorkflowExecutionObserver, OntologyService
Files:
src/synthorg/api/controllers/__init__.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/api/app.py
src/synthorg/persistence/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/persistence/**/*.py: Onlysrc/synthorg/persistence/may import sqlite/psycopg or emit raw SQL; new repository protocols inherit frompersistence/_generics.pycategories
Repository protocols must inherit from generic categories inpersistence/_generics.pyand stay@runtime_checkable; bespoke methods only under ADR-0001 D7
Repository CRUD:save(entity),get(id),delete(id) -> bool,list_items(...),query(...)returning tuples
Datetime in persistence: useparse_iso_utc/format_iso_utcfrompersistence._shared; reject naive dates; usenormalize_utcfor already-typed
Files:
src/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/postgres/backend.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/persistence/sqlite/docs_repo.py
web/src/{api/endpoints,stores}/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Cursor pagination (MANDATORY): list endpoints must use opaque cursor-based paging via
PaginationMeta. Stores must keepnextCursor+hasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursor. Display counts must come fromdata.length.
Files:
web/src/api/endpoints/projectDocs.ts
web/src/api/endpoints/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Health / readiness endpoints (MANDATORY):
getLiveness()is always 200 while the process is alive;getReadiness()is 200 healthy / 503 unavailable (binary'ok' | 'unavailable'outcome, no tri-state). Any new caller must handle the 503 path explicitly.
Files:
web/src/api/endpoints/projectDocs.ts
tests/conformance/persistence/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Dual-backend conformance: tests/conformance/persistence/ consumes backend fixture (SQLite + Postgres); enforced by check_dual_backend_test_parity.py
Files:
tests/conformance/persistence/test_docs_repository.py
🧠 Learnings (5)
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
tests/unit/meta/mcp/test_all_handlers_wired.pytests/unit/observability/test_events.pysrc/synthorg/meta/mcp/handlers/__init__.pysrc/synthorg/tools/docs/__init__.pysrc/synthorg/meta/mcp/domains/__init__.pytests/unit/docs_engine/test_slug.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/observability/events/persistence.pytests/unit/core/test_enums.pysrc/synthorg/docs_engine/serializer.pytests/integration/docs_engine/_workspace.pytests/unit/api/conftest.pysrc/synthorg/persistence/sqlite/_backend_accessors.pysrc/synthorg/docs_engine/errors.pytests/unit/api/fakes.pysrc/synthorg/core/enums.pytests/integration/mcp/test_tool_surface.pytests/unit/docs_engine/test_models.pytests/unit/api/fakes_backend.pysrc/synthorg/persistence/sqlite/backend.pyscripts/check_no_ghost_wiring.pysrc/synthorg/tools/docs/write_living_doc.pysrc/synthorg/docs_engine/slug.pysrc/synthorg/persistence/docs_protocol.pysrc/synthorg/core/error_taxonomy.pysrc/synthorg/docs_engine/tool_factory.pysrc/synthorg/meta/mcp/domains/docs.pysrc/synthorg/persistence/protocol.pytests/integration/docs_engine/test_living_doc_tools.pysrc/synthorg/docs_engine/retrieval_facade.pytests/unit/docs_engine/test_project_doc_category.pytests/unit/api/controllers/test_project_docs.pysrc/synthorg/docs_engine/__init__.pytests/unit/scripts/test_check_schema_drift.pysrc/synthorg/observability/events/docs.pysrc/synthorg/persistence/postgres/backend.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/project_docs.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/docs_engine/constants.pytests/unit/docs_engine/test_indexer.pyscripts/check_schema_drift.pytests/unit/docs_engine/test_serializer.pysrc/synthorg/docs_engine/chunker.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/docs_engine/test_chunker.pysrc/synthorg/meta/mcp/domains/_docs_args.pytests/conformance/persistence/test_docs_repository.pytests/unit/meta/mcp/test_handlers_docs.pysrc/synthorg/docs_engine/writer.pysrc/synthorg/docs_engine/models.pysrc/synthorg/tools/docs/search_living_docs.pysrc/synthorg/api/app.pysrc/synthorg/docs_engine/factory.pysrc/synthorg/docs_engine/indexer.pysrc/synthorg/persistence/postgres/docs_repo.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/docs_engine/service.pysrc/synthorg/tools/docs/_args.pytests/unit/persistence/test_protocol.py
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, do not require adding `<!--RS:...-->` “Doc Numeric Claims (MANDATORY)” numeric macros for Python version numbers mentioned in documentation prose (e.g., “Python 3.14”, “Python 3.15”). The `scripts/check_doc_numeric_macros.py` gate only applies to `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`, and it only flags digits adjacent to specific stat nouns (tests/providers/agents/stars/releases), not language version mentions like “Python 3.14”.
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo, account for the CI gate `check_doc_numeric_macros.py`: it skips fenced code blocks entirely, and it only flags digits that are adjacent to these stat nouns: `tests`, `providers`, `agents`, `stars`, `releases`. Therefore, numeric examples such as CLI flag values (e.g., `--num-workers=4` in fenced bash blocks) and prose version numbers (e.g., `3.14`/`3.15`) are not expected to trigger this check; prioritize changes only when digits appear next to one of the listed nouns (e.g., “5 tests”, “10 stars”, etc.).
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing markdown files for the "Doc Numeric Claims (MANDATORY)" RS-marker rule, only require/flag missing RS markers in the files that are actually in-scope for the rule. The scope is enforced via an identical _SCOPED_FILES allowlist in scripts/check_doc_numeric_macros.py and scripts/inject_runtime_stats.py, and currently includes: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. For any other markdown files (e.g., docs/getting_started.md, docs/guides/*), missing RS markers for numeric claims are no-ops and should NOT be flagged.
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo against the `check_doc_numeric_macros.py` gate, account for its documented behavior: it skips fenced code blocks entirely, and it only flags digits that are adjacent to specific stat nouns (`tests`, `providers`, `agents`, `stars`, `releases`). As a result, CLI-style numbers (e.g., `--num-workers=4`) inside fenced bash code blocks should never be treated as violations of this gate; only non-fenced text needs checking, and only around those specific nouns.
Applied to files:
docs/DESIGN_SPEC.mddocs/design/memory.mddocs/decisions/0001-repository-protocol-consolidation.mddocs/design/page-structure.mddocs/design/living-documentation.md
🪛 LanguageTool
scripts/_ghost_wiring_manifest.txt
[uncategorized] ~68-~68: Do not mix variants of the same word (‘serialize’ and ‘serialise’) within a single text.
Context: ..._engine/factory.py::build_docs_service; serializes doc + commits on synthorg/docs via the ...
(EN_WORD_COHERENCY)
🔇 Additional comments (87)
data/runtime_stats.yaml (1)
1-32: LGTM!tests/unit/meta/mcp/test_all_handlers_wired.py (1)
1-433: LGTM!tests/unit/observability/test_events.py (1)
209-345: LGTM!src/synthorg/meta/mcp/handlers/__init__.py (1)
1-85: LGTM!src/synthorg/tools/docs/__init__.py (1)
1-13: LGTM!web/src/router/routes.ts (1)
1-75: LGTM!web/src/mocks/handlers/index.ts (1)
1-263: LGTM!scripts/schema_drift_baseline.txt (1)
112-113: Past review concern has been resolved.The baseline entries for
project_docs.created_atandproject_docs.updated_atnow have complete, audit-cited justifications following the established pattern. The justifications correctly document that SQLite stores TEXT with ISO-8601 format and explicit UTC offset, normalized at write time—matching the format used by all other TIMESTAMPTZ columns in this baseline (lines 13-110, 114-189).web/src/api/types/enum-values.gen.ts (1)
274-279: LGTM!Also applies to: 434-434
src/synthorg/meta/mcp/domains/__init__.py (1)
13-13: LGTM!Also applies to: 45-45
web/src/pages/project-docs/DocViewer.tsx (1)
1-59: LGTM!web/src/mocks/handlers/projectDocs.ts (1)
1-130: LGTM!tests/unit/docs_engine/test_slug.py (1)
1-40: LGTM!docs/DESIGN_SPEC.md (1)
30-30: LGTM!src/synthorg/api/controllers/__init__.py (1)
67-67: LGTM!Also applies to: 116-116, 246-246
src/synthorg/observability/events/persistence.py (1)
323-344: LGTM!tests/unit/core/test_enums.py (1)
92-96: LGTM!src/synthorg/docs_engine/serializer.py (2)
28-49: LGTM!
52-98: LGTM!tests/integration/docs_engine/_workspace.py (1)
7-26: LGTM!src/synthorg/persistence/postgres/revisions/20260520000001_project_docs.sql (1)
10-37: LGTM!tests/unit/api/conftest.py (2)
74-88: LGTM!
727-734: LGTM!scripts/_ghost_wiring_manifest.txt (1)
58-72: LGTM!src/synthorg/persistence/sqlite/_backend_accessors.py (1)
314-317: LGTM!src/synthorg/docs_engine/errors.py (1)
1-73: LGTM!tests/unit/api/fakes.py (1)
633-692: LGTM!src/synthorg/persistence/sqlite/schema.sql (1)
455-484: LGTM!web/src/pages/project-docs/DocBlockRenderer.tsx (1)
1-109: LGTM!src/synthorg/core/enums.py (1)
121-128: LGTM!Also applies to: 135-135, 138-151
tests/integration/mcp/test_tool_surface.py (1)
1-1: LGTM!Also applies to: 398-402
tests/unit/docs_engine/test_models.py (1)
1-140: LGTM!web/src/api/endpoints/projectDocs.ts (1)
1-62: LGTM!docs/design/memory.md (1)
28-28: LGTM!src/synthorg/persistence/postgres/schema.sql (1)
469-497: LGTM!tests/unit/api/fakes_backend.py (1)
36-36: LGTM!Also applies to: 600-600, 719-721
src/synthorg/persistence/sqlite/backend.py (1)
68-68: LGTM!Also applies to: 210-210, 280-280, 440-443
scripts/check_no_ghost_wiring.py (1)
80-80: LGTM!src/synthorg/tools/docs/write_living_doc.py (1)
1-129: LGTM!Also applies to: 132-147
src/synthorg/docs_engine/slug.py (1)
1-72: LGTM!src/synthorg/persistence/sqlite/revisions/20260520000001_project_docs.sql (1)
1-38: LGTM!src/synthorg/persistence/docs_protocol.py (1)
1-167: LGTM!src/synthorg/core/error_taxonomy.py (1)
74-74: LGTM!Also applies to: 95-95, 115-115, 160-161
docs/decisions/0001-repository-protocol-consolidation.md (1)
181-181: LGTM!src/synthorg/docs_engine/tool_factory.py (1)
1-89: LGTM!src/synthorg/meta/mcp/domains/docs.py (1)
1-160: LGTM!docs/design/page-structure.md (1)
97-98: LGTM!Also applies to: 108-108, 384-385
src/synthorg/persistence/protocol.py (1)
67-67: LGTM!Also applies to: 180-180, 419-423
tests/integration/docs_engine/test_living_doc_tools.py (1)
1-179: LGTM!docs/design/living-documentation.md (1)
1-191: LGTM!src/synthorg/docs_engine/retrieval_facade.py (1)
1-147: LGTM!tests/unit/docs_engine/test_project_doc_category.py (1)
1-100: LGTM!tests/unit/api/controllers/test_project_docs.py (1)
1-139: LGTM!src/synthorg/docs_engine/__init__.py (1)
1-69: LGTM!tests/unit/scripts/test_check_schema_drift.py (1)
890-938: LGTM!web/src/pages/ProjectDocsPage.tsx (1)
1-134: LGTM!src/synthorg/observability/events/docs.py (1)
1-26: LGTM!src/synthorg/persistence/postgres/backend.py (1)
69-69: LGTM!Also applies to: 214-214, 294-295, 358-359, 421-422, 684-688
src/synthorg/api/state.py (1)
150-154: LGTM!Also applies to: 232-234, 282-283, 482-488, 1024-1071
web/src/api/types/dtos.gen.ts (1)
68-370: LGTM!src/synthorg/api/controllers/project_docs.py (1)
1-187: LGTM!src/synthorg/docs_engine/constants.py (1)
1-86: LGTM!tests/unit/docs_engine/test_indexer.py (1)
1-146: LGTM!tests/unit/docs_engine/test_serializer.py (1)
34-109: LGTM!web/src/pages/project-docs/DocList.tsx (1)
16-107: LGTM!src/synthorg/docs_engine/chunker.py (1)
47-67: LGTM!Also applies to: 136-225
web/src/router/index.tsx (1)
34-34: LGTM!Also applies to: 159-160
tests/integration/docs_engine/test_service_round_trip.py (1)
51-337: LGTM!tests/unit/docs_engine/test_chunker.py (1)
44-139: LGTM!src/synthorg/meta/mcp/domains/_docs_args.py (1)
12-80: LGTM!tests/conformance/persistence/test_docs_repository.py (1)
22-205: LGTM!tests/unit/meta/mcp/test_handlers_docs.py (1)
1-224: LGTM!src/synthorg/docs_engine/writer.py (1)
1-270: LGTM!src/synthorg/docs_engine/models.py (1)
1-389: LGTM!src/synthorg/tools/docs/search_living_docs.py (1)
1-145: LGTM!src/synthorg/api/app.py (1)
1147-1195: LGTM!src/synthorg/docs_engine/factory.py (1)
1-87: LGTM!src/synthorg/docs_engine/indexer.py (1)
1-167: LGTM!src/synthorg/persistence/postgres/docs_repo.py (1)
1-348: LGTM!src/synthorg/persistence/sqlite/docs_repo.py (5)
1-42: LGTM!
44-55: LGTM!
57-100: LGTM!
101-132: LGTM!Also applies to: 134-180, 182-209, 211-232
234-269: LGTM!Also applies to: 295-308, 311-318
web/src/api/types/openapi.gen.ts (1)
2675-2742: LGTM!Also applies to: 5217-5224, 5593-5608, 6320-6331, 6577-6590, 7405-7418, 7529-7584, 7776-7776, 8319-8333, 8466-8479, 8493-8527, 8800-8811, 8938-8953, 9338-9352, 10288-10299, 19071-19204
src/synthorg/tools/docs/_args.py (1)
1-195: LGTM!tests/unit/persistence/test_protocol.py (1)
1029-1034: LGTM!
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/synthorg/docs_engine/chunker.py (1)
92-93:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
max_tokensis still effectively not enforced in this path.Line 92 computes
min(target, max), but Line 61 already guaranteesmax >= target, so this stays equal totarget. Large appended prose can still create chunks beyondmax_tokens.Suggested fix
for block in doc.body: if isinstance(block, ProseBlock): - prose_run.append(block) - flush_at = min(self._target_tokens, self._max_tokens) - if self._token_count(_prose_run_text(prose_run)) >= flush_at: + candidate = (*prose_run, block) + if prose_run and self._token_count(_prose_run_text(candidate)) > self._max_tokens: + chunks.append( + self._make_prose_chunk( + project_id=project_id, + doc=doc, + chunk_index=len(chunks), + prose_blocks=prose_run, + base_tags=base_tags, + ) + ) + prose_run = [block] + else: + prose_run.append(block) + if self._token_count(_prose_run_text(prose_run)) >= self._target_tokens: chunks.append( self._make_prose_chunk( project_id=project_id,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/synthorg/docs_engine/chunker.py` around lines 92 - 93, The current flush decision uses flush_at = min(self._target_tokens, self._max_tokens) which is a no-op because self._max_tokens >= self._target_tokens, so large appended prose can exceed max; change the logic to compute the remaining allowed tokens and compare the prose run size against that (e.g. remaining = self._max_tokens - current_chunk_token_count) instead of comparing to self._max_tokens directly, using _token_count(_prose_run_text(prose_run)) >= remaining (or set flush_at = min(self._target_tokens, remaining)); update references to _token_count and _prose_run_text and use the current chunk token count variable/method to enforce _max_tokens.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design/memory.md`:
- Line 55: The table row introducing the numeric claim "4%" lacks the required
RS marker; update the cell containing the claim (the Project doc / "Q3 status
report: checkout flow shipped, retention up 4%") by appending an HTML RS marker
like <!--RS:checkout_retention_up--> immediately after the numeric claim, and
ensure a matching key (checkout_retention_up) with the sourced value exists in
data/runtime_stats.yaml so the README/public doc pulls the sourced statistic
correctly.
---
Duplicate comments:
In `@src/synthorg/docs_engine/chunker.py`:
- Around line 92-93: The current flush decision uses flush_at =
min(self._target_tokens, self._max_tokens) which is a no-op because
self._max_tokens >= self._target_tokens, so large appended prose can exceed max;
change the logic to compute the remaining allowed tokens and compare the prose
run size against that (e.g. remaining = self._max_tokens -
current_chunk_token_count) instead of comparing to self._max_tokens directly,
using _token_count(_prose_run_text(prose_run)) >= remaining (or set flush_at =
min(self._target_tokens, remaining)); update references to _token_count and
_prose_run_text and use the current chunk token count variable/method to enforce
_max_tokens.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6125e7e5-5b42-488f-9d82-f466c6aa0b24
📒 Files selected for processing (9)
docs/design/memory.mdscripts/check_schema_drift.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/docs_engine/service.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/persistence/sqlite/docs_repo.pysrc/synthorg/tools/docs/write_living_doc.pytests/integration/docs_engine/test_service_round_trip.pytests/unit/scripts/test_check_schema_drift.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Deploy Preview
- GitHub Check: Build Backend
- GitHub Check: Build Web Assets (melange)
- GitHub Check: CodSpeed Python benchmarks
- GitHub Check: CodSpeed Web benchmarks
- GitHub Check: Test Conformance (SQLite)
- GitHub Check: Test Integration
- GitHub Check: Test E2E
- GitHub Check: Dashboard Test
- GitHub Check: Test Unit
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Lighthouse Site
- GitHub Check: Analyze (python)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (8)
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Test markers:@pytest.mark.{unit,integration,e2e,slow}; async auto; 30s timeout global; coverage 80% min
Windows: unit tests use WindowsSelectorEventLoopPolicy; subprocess tests override back
Test doubles: FakeClock + mock_ofT from tests._shared; bare MagicMock at typed boundary blocked by scripts/check_mock_spec.py
Hypothesis: 10 deterministic CI examples; failures are real bugs; fix + add@example(...); HYPOTHESIS_PROFILE=dev or fuzz for property tests
Flaky tests: NEVER skip/xfail; fix fundamentally; use asyncio.Event().wait() not sleep(large)
Files:
tests/unit/scripts/test_check_schema_drift.pytests/integration/docs_engine/test_service_round_trip.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/scripts/test_check_schema_drift.pytests/integration/docs_engine/test_service_round_trip.py
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Numeric claims in README and public docs must be sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers
Files:
docs/design/memory.md
**/*.{md,d2}
📄 CodeRabbit inference engine (CLAUDE.md)
Use D2 for architecture/nested containers, mermaid for flowcharts/sequences/pipelines, markdown tables for tabular data; D2 CLI pinned to v0.7.1 in CI
Files:
docs/design/memory.md
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Configuration precedence: DB > env > code default via SettingsService/ConfigResolver (Cat-1) or env > code default (Cat-2); Cat-3 bootstrap secrets pure env; no os.environ.get outside startup
No hardcoded numeric values; allowlist 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants; enforced by scripts/check_no_magic_numbers.py
Comments must explain WHY only; no reviewer citations, issue back-refs, or migration framing; enforced by check_no_review_origin_in_code.py and check_no_migration_framing.py
Nofrom __future__ import annotations; use PEP 758 style exceptions; type hints on public functions; mypy strict mode; Google-style docstrings
Line length 88; functions < 50 lines; files < 800 lines
Error classes:<Domain><Condition>ErrorfromDomainError, never inherit from Exception/RuntimeError; enforced by check_domain_error_hierarchy.py
Pydantic v2 frozen models withextra="forbid"everywhere;@computed_fieldfor derived fields; useNotBlankStrfor identifiers; gate check_frozen_model_extra_forbid.py
Args models at every system boundary; useparse_typed()for external dict ingestion; enforced by check_boundary_typed.py
Immutability: usemodel_copy(update=...)orcopy.deepcopy(); deepcopy at system boundaries
Async: useasyncio.TaskGroupfor fan-out/fan-in; helpers catchException(re-raise MemoryError/RecursionError)
Clock seam:clock: Clock | None = None; tests injectFakeClock; services own_lifecycle_lock; timed-out stops mark unrestartable
Untrusted content (SEC-1): wrap viawrap_untrusted()fromengine.prompt_safety; useHTMLParseGuardfor HTML
Logging: import fromsynthorg.observability.get_logger; variable namelogger; never useimport loggingorprint()in app code
Event names fromobservability.events.<domain>constants; structured kwargs logging; error paths log WARNING/ERROR before raising; state transitions log INFO after persistence
Secret-log redaction...
Files:
src/synthorg/tools/docs/write_living_doc.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/docs_engine/service.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/persistence/sqlite/docs_repo.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/tools/docs/write_living_doc.pysrc/synthorg/docs_engine/chunker.pysrc/synthorg/docs_engine/service.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/persistence/sqlite/docs_repo.py
src/synthorg/meta/mcp/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
MCP: 200+ tools under
meta/mcp/domains/; define ToolHandler + args_model; callrequire_admin_guardrails()on admin tools; route through service layers per mcp-handler-contract.md
Files:
src/synthorg/meta/mcp/handlers/docs.py
src/synthorg/meta/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Conversational propose (ChiefOfStaffProposer): POST /meta/chat/propose is opt-in; built by
build_chief_of_staff_proposerwith ENFORCED manifest entry; 503s if provider/persistence/pipeline missing; wrap human content viawrap_untrusted(TAG_TASK_DATA, ...)
Files:
src/synthorg/meta/mcp/handlers/docs.py
src/synthorg/persistence/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/persistence/**/*.py: Onlysrc/synthorg/persistence/may import sqlite/psycopg or emit raw SQL; new repository protocols inherit frompersistence/_generics.pycategories
Repository protocols must inherit from generic categories inpersistence/_generics.pyand stay@runtime_checkable; bespoke methods only under ADR-0001 D7
Repository CRUD:save(entity),get(id),delete(id) -> bool,list_items(...),query(...)returning tuples
Datetime in persistence: useparse_iso_utc/format_iso_utcfrompersistence._shared; reject naive dates; usenormalize_utcfor already-typed
Files:
src/synthorg/persistence/sqlite/docs_repo.py
🧠 Learnings (5)
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
scripts/check_schema_drift.pytests/unit/scripts/test_check_schema_drift.pysrc/synthorg/tools/docs/write_living_doc.pysrc/synthorg/docs_engine/chunker.pytests/integration/docs_engine/test_service_round_trip.pysrc/synthorg/docs_engine/service.pysrc/synthorg/meta/mcp/handlers/docs.pysrc/synthorg/persistence/sqlite/docs_repo.py
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, do not require adding `<!--RS:...-->` “Doc Numeric Claims (MANDATORY)” numeric macros for Python version numbers mentioned in documentation prose (e.g., “Python 3.14”, “Python 3.15”). The `scripts/check_doc_numeric_macros.py` gate only applies to `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`, and it only flags digits adjacent to specific stat nouns (tests/providers/agents/stars/releases), not language version mentions like “Python 3.14”.
Applied to files:
docs/design/memory.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo, account for the CI gate `check_doc_numeric_macros.py`: it skips fenced code blocks entirely, and it only flags digits that are adjacent to these stat nouns: `tests`, `providers`, `agents`, `stars`, `releases`. Therefore, numeric examples such as CLI flag values (e.g., `--num-workers=4` in fenced bash blocks) and prose version numbers (e.g., `3.14`/`3.15`) are not expected to trigger this check; prioritize changes only when digits appear next to one of the listed nouns (e.g., “5 tests”, “10 stars”, etc.).
Applied to files:
docs/design/memory.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing markdown files for the "Doc Numeric Claims (MANDATORY)" RS-marker rule, only require/flag missing RS markers in the files that are actually in-scope for the rule. The scope is enforced via an identical _SCOPED_FILES allowlist in scripts/check_doc_numeric_macros.py and scripts/inject_runtime_stats.py, and currently includes: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. For any other markdown files (e.g., docs/getting_started.md, docs/guides/*), missing RS markers for numeric claims are no-ops and should NOT be flagged.
Applied to files:
docs/design/memory.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo against the `check_doc_numeric_macros.py` gate, account for its documented behavior: it skips fenced code blocks entirely, and it only flags digits that are adjacent to specific stat nouns (`tests`, `providers`, `agents`, `stars`, `releases`). As a result, CLI-style numbers (e.g., `--num-workers=4`) inside fenced bash code blocks should never be treated as violations of this gate; only non-fenced text needs checking, and only around those specific nouns.
Applied to files:
docs/design/memory.md
🔇 Additional comments (8)
scripts/check_schema_drift.py (1)
308-315: LGTM!tests/unit/scripts/test_check_schema_drift.py (1)
941-960: LGTM!src/synthorg/tools/docs/write_living_doc.py (1)
35-35: LGTM!Also applies to: 132-132
tests/integration/docs_engine/test_service_round_trip.py (1)
247-260: LGTM!src/synthorg/docs_engine/service.py (1)
436-441: LGTM!Also applies to: 553-555
src/synthorg/meta/mcp/handlers/docs.py (1)
15-19: LGTM!Also applies to: 77-77, 169-186, 224-224, 260-260, 291-294, 326-328, 357-359
src/synthorg/persistence/sqlite/docs_repo.py (2)
246-249: LGTM!Also applies to: 275-276
323-347: LGTM!
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2028 +/- ##
==========================================
- Coverage 84.95% 84.94% -0.02%
==========================================
Files 1991 2016 +25
Lines 118250 119582 +1332
Branches 10014 10084 +70
==========================================
+ Hits 100460 101578 +1118
- Misses 15310 15492 +182
- Partials 2480 2512 +32 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…concurrency, tests, docs)
5f70ddc to
c7b34b5
Compare
All findings across three CodeRabbit review rounds (28 -> 10 -> 2 -> 0) are resolved; the rolling summary reports no actionable comments on the current head. Dismissing the stale CHANGES_REQUESTED state.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
<!-- HIGHLIGHTS_START --> ## Highlights > _AI-generated summary (model: `openai/gpt-4.1-mini` via GitHub Models). Commit-based changelog below._ ### What you'll notice - Introduced conversational interface for direct clarify and propose interactions. - Cost management now includes forecast gates, hard ceilings, and Pareto considerations. - Added living documentation engine combining wiki and retrieval-augmented generation features. - Real intake engine is now operational for live data processing. - Virtual desktop tool with vision verification gate available for enhanced workspace control. ### What's new - Per-project reproducible environments for consistent setups. - Headless browser testing tool integrated for automated UI validation. - Governed external API and data access tool introduced. - Hardened external-remote git backend with sandbox mounts and push-queue dispatching. - Adversarial red-team gate subsystem for enhanced security testing. - Self-extending toolkit to dynamically expand capabilities. - Stakes-aware model routing enables prioritized processing. - Task-board entry adapter connects live runtime with project management. - Persistent project workspace with pluggable git backend and per-project push queues implemented. - Knowledge and provenance substrate added to track data lineage. - Scoring and data contract framework for golden-company benchmark evaluations. ### Under the hood - Desktop Dockerfile pinned by digest to improve build stability and documented publishing gap fixed. <!-- HIGHLIGHTS_END --> :robot: I have created a release *beep* *boop* --- ## [0.8.7](v0.8.6...v0.8.7) (2026-05-22) ### Features * conversational interface v1 - 1:1 clarify + propose ([#2019](#2019)) ([216ef94](216ef94)), closes [#1968](#1968) * cost as a first-class dial (forecast gate, hard ceiling, Pareto) ([#2029](#2029)) ([700a59e](700a59e)), closes [#1982](#1982) * **env:** reproducible per-project environments ([#2039](#2039)) ([d2c0ef9](d2c0ef9)), closes [#1994](#1994) * **evals:** [#1980](#1980) spine -- scoring + data contract for golden-company benchmark ([#2025](#2025)) ([53108e8](53108e8)) * goal/objective entry adapter ([#1964](#1964)) ([#2022](#2022)) ([cb15c3c](cb15c3c)) * governed external API/data access tool ([#1991](#1991)) ([#2032](#2032)) ([e08b451](e08b451)) * harden external-remote git backend + per-project sandbox mount + push-queue dispatch ([#2020](#2020)) ([#2030](#2030)) ([2fa2e1e](2fa2e1e)) * headless browser testing tool ([#1992](#1992)) ([#2024](#2024)) ([277b52a](277b52a)) * knowledge + provenance substrate ([#2036](#2036)) ([48c897b](48c897b)) * living documentation engine (dual-purpose wiki + RAG namespace) ([#2028](#2028)) ([3d10da9](3d10da9)), closes [#1976](#1976) * real intake engine online ([#2017](#2017)) ([9d8eb34](9d8eb34)) * **redteam:** adversarial red-team gate subsystem ([#1986](#1986)) ([#2026](#2026)) ([d2207e9](d2207e9)) * self-extending toolkit ([#1995](#1995)) ([#2035](#2035)) ([5ffc545](5ffc545)) * stakes-aware model routing ([#1998](#1998)) ([#2038](#2038)) ([9b98312](9b98312)) * task-board entry adapter to live runtime ([#1963](#1963)) ([#2023](#2023)) ([a8f1eea](a8f1eea)) * virtual desktop tool and vision verifier gate ([#2031](#2031)) ([dfe8b42](dfe8b42)), closes [#1993](#1993) * **workspace:** persistent project workspace + pluggable git backend + per-project push queue ([#2021](#2021)) ([ee58ee7](ee58ee7)) ### Bug Fixes * pin desktop Dockerfile by digest (Scorecard [#309](#309)) + document publish gap ([#2034](#2034)) ([8fda188](8fda188)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: synthorg-repo-bot[bot] <279117679+synthorg-repo-bot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Child of EPIC #1973 (autonomous product studio substrate); rides on the persistent project workspace from #1974.
Gives a synthetic org a first-class place to put what it produces. A living document is a structured Pydantic model stored as JSON in the project git workspace, versioned on a dedicated
synthorg/docsbranch, and chunked into the memory pipeline as a first-class RAG namespace (MemoryCategory.PROJECT_DOC). The same doc is browsable in the dashboard wiki AND retrievable by any agent on a later task.Acceptance criteria (all met)
WriteLivingDocTool+docs:writeMCP handler); read path serves both the dashboard wiki (REST) and agent retrieval (SearchLivingDocsTool+ transparentProjectAwareMemoryFacade).Surface
src/synthorg/docs_engine/: models (discriminatedDocBlockunion), serializer, chunker, indexer, writer, slug, service, retrieval facade, factory, tool factory.DocsRepositoryprotocol (composite(project_id, slug)key,IdKeyed + FilteredQuery), SQLite + Postgres impls, one yoyo migration per backend (dual-backend conformance-tested).GET /projects/{id}/docs[/{slug}[/history]]and/docs/search.docs:write(admin-gated) +docs:get/list/search/history./projects/:id/docswiki page (doc list + block-aware viewer).docs/design/living-documentation.md; cross-linked from memory.md / DESIGN_SPEC / page-structure / ADR-0001.Pre-PR review fixes folded in
Security:
LinkBlock.urlscheme allowlist (blocksjavascript:/data:/file:stored-XSS);read_docversion validated as a commit-SHA before reachinggit show(argument-injection guard). Concurrency: retrieval facade re-raises cancellation instead of swallowing it; a per-project write lock serialises slug derivation with the write so concurrent same-title writes cannot silently overwrite. Type design: agent-facing block args refactored from an all-optional shape + 46-line validator into a 7-variant discriminated union with per-variantto_block(). Quality:ServiceUnavailableError(503) for an unwired service; bounded indexer drain loop; design-token fixes; typed frontend block union; search-tool error logging. Persistence: tagLIKEfilter escapes%/_.Note on the Postgres repo rollback change: psycopg's pooled-connection context already auto-rolls-back on exit, so this is not a correctness fix; its value is the operator-visible rollback-failure WARNING log, matching the SQLite arm.
Validation
Unit suite green on changed areas; mypy strict clean; all pre-commit + pre-push gates pass; dual-backend conformance (24) and docs-engine integration (13, run with
ALLOW_E2E_TESTS=1) green, including a partial-index-failure recovery test and a concurrent-same-title slug-race test.Closes #1976