Skip to content

feat(renderable): add decodeDocument API for live documentation#44

Merged
darko-mijic merged 2 commits intomainfrom
feature/realtime-docs
Apr 4, 2026
Merged

feat(renderable): add decodeDocument API for live documentation#44
darko-mijic merged 2 commits intomainfrom
feature/realtime-docs

Conversation

@darko-mijic
Copy link
Copy Markdown
Contributor

@darko-mijic darko-mijic commented Apr 3, 2026

Summary

  • Add decodeDocument() and decodeDocumentSafe() — codec decode without markdown rendering. Returns RenderableDocument as structured JSON for native UI rendering in Studio.
  • Export CodecOptions and GenerationError types for typed consumer usage.
  • Fix prepublishOnly to guarantee clean builds: pnpm clean runs first (removes stale tsbuildinfo), new verify:exports script checks all 34 declared paths exist in dist/.

Context

The Live Documentation API needs a way to invoke codecs and get structured RenderableDocument back instead of markdown strings. The existing generateDocument() always flattens to markdown — decodeDocument() stops after the decode step, returning the intermediate format that Studio can render natively (tables, Mermaid diagrams, collapsibles).

The publish safety fix prevents the stale tsbuildinfo issue that caused incomplete dist/ in pre.5.

New public API (@libar-dev/architect/renderable)

decodeDocument(type, dataset, options?)  RenderableDocument
decodeDocumentSafe(type, dataset, options?)  Result<RenderableDocument, GenerationError>

Review hardening (938a6a3)

Multi-agent review (code quality + security + silent failure analysis) identified and fixed:

  • JSDoc accuracy: replaced invalid "design-review" DocumentType in example with "architecture"
  • Path containment: verify-exports.mjs now rejects export paths that resolve outside project root
  • pkg.bin polymorphism: handles both string ("./cli.js") and object ({ "cmd": "./cli.js" }) forms — string form was silently skipped
  • Nested conditional exports: skips with warning instead of producing misleading "MISSING" errors
  • Actionable error messages: "no codec registered" errors now list available document types across all 4 call sites

Test plan

  • Typecheck passes
  • Lint passes
  • All 8798 tests pass (142 files)
  • Clean build produces all 34 declared export paths
  • verify:exports catches missing dist/ (tested with rm -rf dist)
  • Stale tsbuildinfo scenario reproduced and confirmed caught by new pipeline

Summary by CodeRabbit

  • New Features

    • Added two document decoding modes: a safe mode that returns structured errors for graceful handling, and a strict mode that throws on failure; both support optional context enrichment for richer decoding.
  • Bug Fixes / Improvements

    • Improved missing-codec error messages to include the requested document type and a list of currently available codec types for clearer diagnostics.

Add decodeDocument() and decodeDocumentSafe() — the codec decode step
without markdown rendering. Interactive consumers (Studio desktop, MCP
clients) use these to receive typed RenderableDocument blocks for native
UI rendering instead of parsing static markdown.

Also fix prepublishOnly to guarantee clean builds: pnpm clean runs first
(removing stale tsbuildinfo), and a new verify:exports script checks all
34 declared export paths exist in dist/ before publish proceeds.

New public API from @libar-dev/architect/renderable:
- decodeDocument(type, dataset, options?) → RenderableDocument
- decodeDocumentSafe(type, dataset, options?) → Result<RenderableDocument>
- type CodecOptions (codec-specific option union)
- type GenerationError (structured decode/render error)
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

Added two new decoding entry points in src/renderable/generate.ts: decodeDocumentSafe() (returns Result<RenderableDocument, GenerationError>) and decodeDocument() (throws on failure). Both support optional codec context enrichment and ensure cleanup. Also updated missing-codec error messages to include requested type and registered codec list, and re-exported new APIs/types from src/renderable/index.ts.

Changes

Cohort / File(s) Summary
Decoding API Implementation
src/renderable/generate.ts
Added decodeDocumentSafe() and decodeDocument() to return RenderableDocument from codec.decode without markdown rendering; both accept optional contextEnrichment, call setCodecContextEnrichment(), and clean up in a finally block. Updated missing-codec error messages in generateDocumentSafe()/generateDocument() to include the requested document type in quotes and append CodecRegistry.getRegisteredTypes().join(', ').
Public API Exports
src/renderable/index.ts
Re-exported decodeDocument and decodeDocumentSafe, and added type exports for type CodecOptions and type GenerationError.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I nibble bytes and chase the code,

I decode paths where documents go,
Safe or bold, I hop along,
Context set and cleanup strong,
A joyful hop — the render's song.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding new decodeDocument APIs to the renderable module for live documentation purposes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/realtime-docs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderable/generate.ts`:
- Around line 398-406: The JSDoc example passes an invalid DocumentType
("design-review") which is not in DOCUMENT_TYPES and will break consumers;
update the example to use a valid type (e.g., "architecture" or "patterns") for
the call to decodeDocumentSafe, removing the invalid per-document mapping and
calling decodeDocumentSafe("architecture", patternGraph) (referencing the
decodeDocumentSafe function and DOCUMENT_TYPES/RenderableDocument types in the
file) so the example compiles and returns a proper RenderableDocument.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 78be219c-3df5-47f2-b71a-df147a8889db

📥 Commits

Reviewing files that changed from the base of the PR and between 347b24c and ca22720.

⛔ Files ignored due to path filters (2)
  • package.json is excluded by none and included by none
  • scripts/verify-exports.mjs is excluded by none and included by none
📒 Files selected for processing (2)
  • src/renderable/generate.ts
  • src/renderable/index.ts

Multi-agent review findings:
- Fix JSDoc example using invalid "design-review" DocumentType
- Add path containment guard in verify-exports.mjs (prevents traversal)
- Handle pkg.bin string shorthand (was silently skipped)
- Guard nested conditional exports (skip with warning vs misleading error)
- Include available types in "no codec registered" error messages
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/renderable/generate.ts (1)

393-393: ⚠️ Potential issue | 🟡 Minor

JSDoc still references an invalid DocumentType (design-review).

The options param example text mentions design-review, which is not present in DOCUMENT_TYPES. Please replace with an existing type (e.g., architecture or pr-changes) to avoid misleading consumers.

📝 Suggested doc fix
- * `@param` options - Optional codec-specific options (e.g., patternName for design-review)
+ * `@param` options - Optional codec-specific options (e.g., changedFiles for pr-changes)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderable/generate.ts` at line 393, JSDoc for the options parameter in
src/renderable/generate.ts incorrectly references a non-existent DocumentType
"design-review"; update the docstring to mention a valid DOCUMENT_TYPES value
(for example "architecture" or "pr-changes") and ensure the `@param` description
for options accurately reflects supported codec-specific option names (reference
DOCUMENT_TYPES and the options param in the generate function to confirm
available types).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderable/generate.ts`:
- Around line 462-486: The decodeDocument function currently throws on missing
codec and returns decoded value directly; change it to follow the project's
Result-monad convention by returning Result.ok(decoded) or Result.error(err)
instead of throwing: use resolveCodec and if undefined return Result.error with
a message that includes CodecRegistry.getRegisteredTypes(); wrap the
codec.decode(dataset) call so its success returns Result.ok and any runtime
error returns Result.error; preserve context enrichment behavior around
setCodecContextEnrichment and clearCodecContextEnrichment but ensure cleanup
still runs and that errors are captured and converted to Result.error rather
than thrown (refer to decodeDocument, resolveCodec,
CodecRegistry.getRegisteredTypes, setCodecContextEnrichment,
clearCodecContextEnrichment, and codec.decode for locations to change).

---

Duplicate comments:
In `@src/renderable/generate.ts`:
- Line 393: JSDoc for the options parameter in src/renderable/generate.ts
incorrectly references a non-existent DocumentType "design-review"; update the
docstring to mention a valid DOCUMENT_TYPES value (for example "architecture" or
"pr-changes") and ensure the `@param` description for options accurately reflects
supported codec-specific option names (reference DOCUMENT_TYPES and the options
param in the generate function to confirm available types).
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e9747e70-dcbd-4ba4-8794-36e203c95530

📥 Commits

Reviewing files that changed from the base of the PR and between ca22720 and 938a6a3.

⛔ Files ignored due to path filters (1)
  • scripts/verify-exports.mjs is excluded by none and included by none
📒 Files selected for processing (1)
  • src/renderable/generate.ts

Comment on lines +462 to +486
export function decodeDocument(
type: DocumentType,
dataset: PatternGraph,
options?: CodecOptions,
contextEnrichment?: CodecContextEnrichment
): RenderableDocument {
const codec = resolveCodec(type, options);
if (codec === undefined) {
throw new Error(
`No codec registered for document type: "${type}". Available types: ${CodecRegistry.getRegisteredTypes().join(', ')}`
);
}

if (contextEnrichment) {
setCodecContextEnrichment(contextEnrichment);
}

try {
return codec.decode(dataset) as RenderableDocument;
} finally {
if (contextEnrichment) {
clearCodecContextEnrichment();
}
}
}
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

New/updated throw paths conflict with the repository’s Result-based error-handling rule.

decodeDocument() is introduced as exception-based, and the touched generateDocument() path still throws. This diverges from the project’s required Result-monad convention.

♻️ Suggested direction
-export function decodeDocument(
+export function decodeDocument(
   type: DocumentType,
   dataset: PatternGraph,
   options?: CodecOptions,
   contextEnrichment?: CodecContextEnrichment
-): RenderableDocument {
-  const codec = resolveCodec(type, options);
-  if (codec === undefined) {
-    throw new Error(
-      `No codec registered for document type: "${type}". Available types: ${CodecRegistry.getRegisteredTypes().join(', ')}`
-    );
-  }
-
-  if (contextEnrichment) {
-    setCodecContextEnrichment(contextEnrichment);
-  }
-
-  try {
-    return codec.decode(dataset) as RenderableDocument;
-  } finally {
-    if (contextEnrichment) {
-      clearCodecContextEnrichment();
-    }
-  }
+): Result<RenderableDocument, GenerationError> {
+  return decodeDocumentSafe(type, dataset, options, contextEnrichment);
 }

As per coding guidelines, Use Result monad for explicit error handling: functions should return Result.ok(value) or Result.error(err) instead of throwing exceptions.

Also applies to: 605-607

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderable/generate.ts` around lines 462 - 486, The decodeDocument
function currently throws on missing codec and returns decoded value directly;
change it to follow the project's Result-monad convention by returning
Result.ok(decoded) or Result.error(err) instead of throwing: use resolveCodec
and if undefined return Result.error with a message that includes
CodecRegistry.getRegisteredTypes(); wrap the codec.decode(dataset) call so its
success returns Result.ok and any runtime error returns Result.error; preserve
context enrichment behavior around setCodecContextEnrichment and
clearCodecContextEnrichment but ensure cleanup still runs and that errors are
captured and converted to Result.error rather than thrown (refer to
decodeDocument, resolveCodec, CodecRegistry.getRegisteredTypes,
setCodecContextEnrichment, clearCodecContextEnrichment, and codec.decode for
locations to change).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — the dual-API pattern is deliberate and pre-existing.

This codebase provides paired variants throughout:

  • generateDocumentSafe()Result<OutputFile[], GenerationError>
  • generateDocument() → throws, returns OutputFile[]

The new decode functions mirror this exact convention:

  • decodeDocumentSafe()Result<RenderableDocument, GenerationError>
  • decodeDocument() → throws, returns RenderableDocument

The Safe suffix is the naming contract that distinguishes Result-based from throwing variants. The Result monad itself includes unwrap() (converts Result back to throwing), confirming both patterns are valid.

Making decodeDocument return Result would make it identical to decodeDocumentSafe, eliminating consumer choice and breaking the established Foo/FooSafe API symmetry that shipped in PR #40.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darko-mijic, you're absolutely right — I missed the established Foo / FooSafe dual-API convention. Converting decodeDocument() to return Result would collapse it into decodeDocumentSafe(), eliminating the throwing variant that some consumers prefer. My suggestion was incorrect; the symmetry is correct as-is.

ʕ •ᴥ•ʔ (learning noted for future reviews)


✏️ Learnings added
Learnt from: darko-mijic
Repo: libar-dev/architect PR: 44
File: src/renderable/generate.ts:462-486
Timestamp: 2026-04-04T09:22:47.625Z
Learning: In libar-dev/architect, the codebase deliberately provides paired API variants throughout `src/renderable/generate.ts`:
- The throwing variant (e.g., `generateDocument()`, `decodeDocument()`) returns the value directly and throws on error.
- The `Safe` suffixed variant (e.g., `generateDocumentSafe()`, `decodeDocumentSafe()`) returns `Result<T, GenerationError>` for explicit error handling.
This `Foo` / `FooSafe` naming contract is intentional and must not be collapsed into a single Result-returning function. Do NOT flag the throwing variants as violating the Result monad convention — both patterns are valid by design.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: libar-dev/architect PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T02:23:21.646Z
Learning: Applies to src/{generators,renderable}/**/*.ts : Use Codec-Based Rendering: generators in `src/generators/` must use codecs to transform data to markdown via Zod codecs

Learnt from: CR
Repo: libar-dev/architect PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T02:23:21.646Z
Learning: Applies to src/renderable/codecs/**/*.ts : TypeScript files that implement codecs must use Zod schemas for transformation. Codecs transform data structures to markdown using schema definitions.

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/renderable/codecs/validation-rules.ts:232-235
Timestamp: 2026-04-01T14:42:40.489Z
Learning: In `src/renderable/codecs/validation-rules.ts`, `ValidationRulesCodec` intentionally ignores the `CodecContext` (passed as `_context`) because it generates documentation for static, built-in `RULE_DEFINITIONS` (Process Guard validation rules), not for user-extracted source data. The TODO comment in the file explicitly documents this design decision.

Learnt from: CR
Repo: libar-dev/architect PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T02:23:21.646Z
Learning: Applies to **/*.{ts,tsx} : Use Result monad for explicit error handling: functions should return `Result.ok(value)` or `Result.error(err)` instead of throwing exceptions

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/renderable/codecs/validation-rules.ts:232-235
Timestamp: 2026-04-01T14:42:29.625Z
Learning: When referencing a function by name in review feedback (or proposing a call site), don’t only check that the function exists—verify the proposed invocation matches the function’s actual exported signature. Confirm argument order and the expected parameter types (and any overloaded variants) against the definition. For example, in src/renderable/codecs/validation-rules.ts, composeRationaleIntoRules expects (rules: readonly RuleDefinition[], conventionContent: ReadonlyMap<string, {...}>)—so calls should pass rules first and the convention map second, not a CodecContext as the second argument.

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/renderable/codecs/types/base.ts:206-224
Timestamp: 2026-04-01T14:42:08.890Z
Learning: When reviewing JavaScript/Node.js code, do NOT suggest thread-safety or race-condition warnings for code that is explicitly synchronous and runs on the main single-threaded JS execution model. In particular, module-level or shared mutable state that is used only within a synchronous generation/decode pipeline (e.g., set codec context before a synchronous decode call, then cleared immediately after) does not imply a race condition under the stated synchronous execution contract. Avoid “speculative” warnings about hypothetical future async/worker scenarios; follow the project philosophy of not adding error handling, fallbacks, or validation for scenarios that cannot happen. If the code already documents the synchronous execution guarantee (e.g., via JSDoc), treat that as sufficient justification.

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/renderable/codecs/pr-changes.ts:6-6
Timestamp: 2026-04-01T14:42:07.229Z
Learning: In this repository, scanner opt-in and runtime dependency annotations must use the `architect` prefix (e.g., `architect`). Do NOT suggest or add deprecated/invalid tags such as `libar-docs` or `libar-docs-uses`. For TypeScript files, opt into scanning by placing `architect` in the JSDoc header; if runtime dependency annotations are present, they should follow the same `architect` prefix convention.

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/renderable/render-options.ts:21-21
Timestamp: 2026-04-01T14:42:10.736Z
Learning: In the `architect` TypeScript codebase (especially `src/renderable/**/*.ts`), do not suggest or introduce `Object.freeze` for empty default objects typed via `readonly` properties (e.g., `DEFAULT_SIZE_BUDGET: SizeBudget = {}`). If the interface/type already uses `readonly` and the object has no mutable runtime fields, freezing an empty object provides no meaningful runtime protection (effectively a no-op). Reserve `Object.freeze` for objects that contain actual mutable properties that must be protected at runtime.

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/taxonomy/arch-values.ts:1-9
Timestamp: 2026-04-01T14:42:13.881Z
Learning: In this repository (libar-dev/architect), the documentation-scanner opt-in marker is `architect` (used in a file’s JSDoc header). Do not suggest or add the old `libar-docs` prefix—this rebrand marker no longer exists in the codebase. If a file already has `architect` in its JSDoc header, it is correctly opted in for documentation scanning and should not be flagged for opt-in/marker issues.

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: tests/features/doc-generation/index-codec.feature:1-6
Timestamp: 2026-04-01T14:42:18.034Z
Learning: In the libar-dev/architect repository, the old documentation tag prefix `libar-docs-*` no longer exists (it was renamed during a rebrand). Do NOT flag missing or incorrect `libar-docs-*` tags in this repo. Instead, expect the `architect-*` tag prefix in both feature files and TypeScript sources (e.g., `architect-pattern`, `architect-status`, `architect-product-area`, `architect-unlock-reason`, etc.).

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/renderable/split.ts:72-105
Timestamp: 2026-04-01T14:42:14.584Z
Learning: When reviewing heuristic or budget-based logic (e.g., line-count/document splitting or other approximate heuristics), treat measurement imprecision within the intended heuristic bounds as expected behavior, not a bug. Only flag violations that are true hard correctness requirements (must-never conditions); distinguish them from soft heuristics that are approximate by design to avoid false positives.

Learnt from: darko-mijic
Repo: libar-dev/architect PR: 40
File: src/renderable/codecs/validation-rules.ts:232-235
Timestamp: 2026-04-01T14:42:29.625Z
Learning: When reviewing the Architect codebase and proposing changes to a codec or function, first scan nearby code for TODO comments or inline documentation that explicitly explains intentional behavior/design decisions (e.g., unused parameters preserved for interface consistency). If the ignored/unused parameter (or similar behavior) is explicitly documented as intentional, do not raise it as a problem—treat it as a likely false positive.

@darko-mijic darko-mijic merged commit 8a54988 into main Apr 4, 2026
4 checks passed
@darko-mijic darko-mijic deleted the feature/realtime-docs branch April 4, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant