Purpose
-------
Workspace (monorepo) support for OCT (F1, leanest first cut). A
workspace is a directory containing a ``.option_c_workspace.json``
manifest and one or more Option C subprojects. ``oct git`` commands
invoked from a workspace root may operate on the entire repo without
the ``--root-dir <subproj> --allow-outside-project`` escape hatch
required pre-F1.
Responsibilities
----------------
- Define :class:`Workspace` - the loaded representation of the
manifest.
- Provide :func:`load_workspace` - read and validate the manifest at
``<root>/.option_c_workspace.json``.
- Provide :func:`find_workspace_root` - walk upward from a starting
directory to locate the nearest workspace manifest.
- Be intentionally conservative: invalid manifests raise
:class:`WorkspaceError` rather than degrading silently.
Diagnostics
-----------
Domain: WORKSPACE
Levels:
L1 - errors (invalid manifest, IO failure)
L2 - lifecycle (load start / end)
L3 - details (resolved subproject paths)
L4 - verbose tracing (workspace-root walk misses)
Contracts
---------
- This module imports nothing from ``oct.git`` or ``oct.cli``; it sits
beneath them in the dependency graph.
- Manifest schema is JSON-only (no TOML); the file is small enough
that adding a TOML parser is unwarranted.
- Subproject paths in the manifest are interpreted relative to the
workspace root. Absolute paths in the manifest are rejected.
- F1 fan-out (v0.25.0): the manifest now also carries
``workspace_profile`` (default ``"compact"``), ``workspace_files``
(gitignore-style globs of files owned by the workspace itself
rather than any subproject), and ``ignore_at_workspace_scope``
(extra ignore patterns applied at the workspace ``.gitignore``).
All three are optional with documented defaults.
- Plan 209: a subproject entry's ``nested_git`` must be a JSON boolean
or omitted (default ``False``); any other type raises
:class:`WorkspaceError` rather than being silently coerced, the same
strictness already applied to ``profile``.
- 2026-09-26: a subproject entry may carry ``html_docs`` to declare its
participation in the generated docs site. It is the ONLY control over
which subprojects ``oct docs`` builds and cards; there is deliberately
no workspace-level exclusion pattern list, so precedence between two
competing declarations can never arise. Accepted shapes are a boolean
or the object ``{"hub": <bool>}``; anything else raises
:class:`WorkspaceError`.
- 2026-09-28: the ``html_docs`` object also accepts ``"doxygen": <bool>``
(default ``True``), which decides whether the subproject's Doxygen
output is part of the generated docs site. It is stored as
:attr:`Subproject.html_docs_doxygen`; ``html_docs: false`` forces it
``False``. It governs only the workspace fan-out: a direct
``oct docs --doxygen`` inside the subproject still builds. Unknown keys
are still rejected, and the message names every supported key.
Dependencies
------------
- oct.core.diagnostics (_dbg structured logger)
| tuple[bool, bool, bool] workspace._parse_html_docs |
( |
object | raw, |
|
|
int | idx ) |
|
protected |
Parse a subproject's ``html_docs`` declaration.
Parameters
----------
raw
The raw manifest value, or ``None`` when the key is absent.
idx
Index of the owning ``subprojects[]`` entry, used only to make
the error message point at the offending entry.
Returns
-------
tuple[bool, bool, bool]
``(html_docs, html_docs_hub, html_docs_doxygen)``.
Raises
------
WorkspaceError
On any shape other than ``None``, a boolean, or an object whose
keys are drawn from :data:`_HTML_DOCS_KEYS` and each mapped to a
boolean. Rejecting unknown keys rather than ignoring them means a
typo such as ``{"hubb": false}`` fails loudly instead of quietly
leaving the card in place, which is the failure mode the operator
would have had to notice by eye on the rendered page.
Definition at line 206 of file workspace.py.
| tuple[bool, str] workspace._validate_workspace_pattern |
( |
object | pattern | ) |
|
|
protected |
Return ``(valid, message)`` for a workspace_files / ignore pattern.
Mirrors ``oct.git.policy.validate_ignore_pattern`` so the manifest
surface is held to the same safety bar as ``oct git ignore``: no
negation, no absolute paths, no shell metacharacters, no newlines.
Definition at line 509 of file workspace.py.