PRD: Unified Content Publishing Platform
Document Control
| Field | Value |
|---|---|
| Version | 1.2 |
| Status | Active (MVP Complete) |
| Author | Rohit |
| Last Updated | 2026-03-06 |
Executive Summary
LOW CONFIDENCE -- Problem not validated. The problem statement in this PRD has not been confirmed with quantified data, stakeholder input, or impact metrics. Treat requirements as hypotheses until validation is completed.
Symphony Core publishes internal documentation across multiple Docusaurus sites (team.symphonycore.com, dev.symphonycore.com) using a sync tool that copies markdown from a single source repo. The tool cannot clean up archived content, only supports one source repo, and requires maintaining two nearly identical publishing repositories. This PRD defines a unified content publishing platform that consolidates multiple source repos, multiple target sites, stale content cleanup, structured data publishing, and audience-segmented access into a single extensible pipeline.
Scope Tier: 1 -- New Product/System
Goals & Desired Outcomes
Business Outcomes
| Outcome | Measurement | Target | Timeline |
|---|---|---|---|
| Eliminate stale/archived content from published sites | Count of orphaned pages on published sites | 0 stale pages after any sync run | MVP |
| Reduce infrastructure duplication across wiki repos | Number of separate publishing repos maintained | 1 unified repo (down from 2) | MVP |
| Enable publishing from any configured source | Number of distinct source repos syncing successfully | 3+ sources (documents, dev repos, structured data) | Phase 2 |
| Support audience-segmented publishing | Number of distinct target sites with independent access policies | 3+ (team, dev, partner) | Phase 2 |
User Outcomes
| User Persona | Current Pain | Desired Future State |
|---|---|---|
| Documentation author | Must manually track which docs are published where; no feedback on stale content | Tag a document with publish_to, run sync, and trust that published sites reflect current source state exactly |
| Platform operator | Maintains two separate repos with identical infrastructure; sync tool has no cleanup | Single repo to maintain; sync is idempotent -- adds, updates, and removes in one pass |
| Developer | READMEs in dev repos are not discoverable on dev.symphonycore.com | READMEs from configured repos auto-publish to developer docs site |
| Business stakeholder | No way to share confidential docs with partners via a controlled portal | Partner-facing site with Cloudflare Access invite-only auth publishes tagged content |
Success Looks Like
A documentation author writes or updates a markdown file in any configured source repo, sets publish_to: [internal-wiki, developer-docs] in frontmatter, and runs npm run sync. Within seconds, the content appears on the correct sites. If a document is moved to the archive repo or has its publish_to tag removed, the next sync run automatically removes it from all target sites. The platform operator manages one repository, one config file, and one deployment pipeline for all wiki sites. A developer's README in their project repo shows up on dev.symphonycore.com without any manual copying.
Context
| Attribute | Value |
|---|---|
| Use Case | Work |
| Audience | Internal (team, dev), Restricted External (partner), Public (help -- future) |
| Platform | Web (Docusaurus static sites on Cloudflare Pages) |
User Personas
Persona 1: Documentation Author
- Description: Team member who writes SOPs, reference docs, training materials in symphony-core-documents or other source repos
- Goals: Publish content to the right audience with minimal friction; trust that archived content disappears
- Pain Points: No cleanup of stale content; must remember which repo targets which site; no visibility into what's published where
Persona 2: Platform Operator (Rohit)
- Description: Maintains publishing infrastructure, deployment pipelines, Cloudflare configuration
- Goals: Minimize maintenance overhead; single source of truth for publishing config; reliable, idempotent sync
- Pain Points: Two nearly identical repos (internal-docs, developer-docs); sync tool lives in one repo but serves both; no state tracking or cleanup
Persona 3: Developer
- Description: Writes code and maintains READMEs in development repositories
- Goals: Have project documentation discoverable on dev.symphonycore.com without manual publishing steps
- Pain Points: READMEs stay in GitHub; not surfaced on the internal developer docs site
Persona 4: Business Partner (Future)
- Description: External partner who needs access to specific business-confidential documents
- Goals: Access relevant partner documentation via a simple, secure portal
- Pain Points: No controlled sharing mechanism exists; documents shared ad-hoc via email or Drive
Jobs-To-Be-Done (JTBD)
Primary Job
When I have documentation spread across multiple repos and need it published to audience-appropriate websites, I want to tag documents with their intended audience and run a single sync command, so I can trust that each site reflects the current, correct set of documents for its audience.
Job Components
- Functional: Sync tagged content from multiple sources to multiple target sites, removing stale content and transforming formats as needed
- Emotional: Confidence that published sites are accurate and current; no anxiety about stale or misrouted content
- Social: Team and partners see a professional, well-maintained documentation platform
Current Alternatives
- Manual copy-paste: Sync tool copies files but doesn't clean up; stale content requires manual deletion
- Two separate repos: internal-docs and developer-docs maintained independently with shared sync-tool dependency
- Ad-hoc sharing: Partner documents shared via email or Google Drive links
- Third-party KB: help.symphonycore.com hosted externally; no SEO ownership, no content control
Job Success Criteria
- Running
npm run syncproduces a deterministic, reproducible result: published sites match tagged source content exactly - Adding a new source repo or target site requires only configuration changes, no code changes
- Removing a document from source (or un-tagging it) results in removal from published sites on next sync
Problem Validation
Evidence of Problem
- Customer interviews conducted: N/A (internal tooling)
- Usage data analyzed: Sync tool source code confirms no cleanup logic exists (publisher.ts is append/update only)
- Support ticket analysis: N/A -- problem identified through codebase analysis
- Market research: N/A
- Other evidence: Two repos (internal-docs, developer-docs) share ~90% identical infrastructure; developer-docs has no sync tool and depends on cross-repo invocation
Problem Statement
LOW CONFIDENCE -- Problem not validated. No quantified data on stale content impact or maintenance overhead. Treat as hypothesis until validated by a full sync audit.
Current State: Symphony Core publishes documentation using a sync tool (sync-tool/) that scans a single source repo (symphony-core-documents) for files tagged with publish_to in YAML frontmatter and copies them to target repos. Two separate Docusaurus repos (internal-docs at team.symphonycore.com, developer-docs at dev.symphonycore.com) are maintained with near-identical configuration. The sync tool is append/update only -- it writes files to destinations but never removes files that no longer exist in the source.
Pain Points:
- Stale content persists: Documents archived (moved to archive repo) or un-tagged remain published indefinitely
- Infrastructure duplication: Two repos with identical stack (Docusaurus + Cloudflare Pages + Cloudflare Access) maintained separately
- Single-source limitation: Only symphony-core-documents is supported; dev repo READMEs and structured data (client registry) cannot be published
- No partner portal: No mechanism for controlled, audience-segmented publishing of business-confidential content
- Cross-repo sync dependency: developer-docs has no sync tool; depends on invoking internal-docs' tool with
--target developer-docs --dest ../developer-docs
Impact: Manual effort to audit and clean stale content; risk of outdated/archived documentation remaining accessible; duplicated maintenance work across repos; inability to publish content from additional sources.
Root Cause Analysis
| Level | Question | Finding |
|---|---|---|
| Surface | What problem was initially stated? | Sync tool can't clean up stale docs, only supports one source, requires two repos |
| Why 1 | Why does stale content persist? | Tool was built as a one-way copy mechanism with no state tracking or deletion logic |
| Why 2 | Why is it one-way with no state tracking? | Originally, archival was handled by a status flag; when that moved to a separate archive repo, the tool wasn't updated |
| Why 3 | Why are there two separate repos with identical infrastructure? | Each wiki site was set up independently as needs arose |
| Why 4 | Why were they set up independently? | No unified content publishing platform was designed; infrastructure was bootstrapped incrementally |
Root Cause (deepest actionable level): Symphony Core lacks a unified content publishing platform -- publishing infrastructure was bootstrapped incrementally per-site, resulting in duplicated repos, a single-source sync tool with no cleanup, and no path to scale to additional sources or audiences.
Stop Reason: Further "why" leads to organizational growth patterns outside sphere of influence.
Opportunity Solution Tree
Desired Outcome (Business Goal)
Establish a single, extensible content publishing platform that routes documents from any configured source to any configured audience-specific site, with accurate cleanup of stale content.
Opportunities (Customer Needs)
| Opportunity ID | Customer Need/Pain Point | Evidence | Impact Potential |
|---|---|---|---|
| OPP-001 | Published sites must reflect current source state (no stale content) | Sync tool has no deletion logic; archived docs persist on sites | High |
| OPP-002 | Reduce infrastructure maintenance by consolidating duplicate repos | internal-docs and developer-docs share ~90% identical config | High |
| OPP-003 | Publish content from multiple source types (markdown repos, READMEs, structured data) | Only symphony-core-documents supported; dev READMEs and client registry cannot publish | Medium |
| OPP-004 | Audience-segmented publishing with appropriate access controls | No partner portal; all content goes to two internal sites | Medium |
| OPP-005 | Publishable structured data (YAML/JSON to rendered pages) | Client registry is YAML; no pipeline to render it as documentation | Low |
| OPP-006 | Customer-facing KB with SEO ownership | help.symphonycore.com hosted by third party; no SEO benefit | Low (research phase) |
Solutions (How We Address Opportunities)
| Solution ID | Addresses Opportunity | Description | Selected |
|---|---|---|---|
| SOL-001 | OPP-001 | Manifest-based sync with diff: track previously synced files and remove orphans | Yes |
| SOL-002 | OPP-001 | Clean-slate sync: delete target directory before every sync | No -- destroys manually-authored content (e.g., CLI catalog in developer-docs) |
| SOL-003 | OPP-002 | Consolidate into single repo with multiple Docusaurus site builds, each deployed to its own Cloudflare Pages project/subdomain | Yes |
| SOL-004 | OPP-002 | Keep repos separate, share config via npm package | No -- doesn't reduce maintenance meaningfully |
| SOL-005 | OPP-003 | Extend sync tool with pluggable source adapters (git repo, README scanner, YAML transformer) | Yes |
| SOL-006 | OPP-004 | Add target site definitions in config with per-site Cloudflare Access policies | Yes |
| SOL-007 | OPP-005 | Add a YAML-to-markdown renderer as a source adapter | Yes |
| SOL-008 | OPP-006 | Stand up public Docusaurus site at help.symphonycore.com with SEO config | Deferred -- research only |
Why This Solution?
Manifest-based sync (SOL-001) over clean-slate (SOL-002) because developer-docs contains manually-authored CLI catalog pages that must not be deleted during sync. A manifest tracks what the sync tool owns and only cleans those files.
Repo consolidation with multi-site builds (SOL-003) over shared config (SOL-004) because the two repos have identical deployment pipelines, identical auth, and overlapping sync dependencies. Each subdomain (team, dev, partner) keeps its own Cloudflare Access policy and independent deployment, but all site definitions, content, and the sync tool live in one repo. This preserves access separation (different people access dev vs team) while eliminating cross-repo dependencies.
Pluggable source adapters (SOL-005) because source types are heterogeneous: markdown repos, individual README files, YAML data files. A common interface with specialized adapters keeps the core pipeline clean.
MVP Definition
In Scope (MVP)
- Stale content cleanup: Manifest-based tracking of synced files; orphan removal on every sync run (first run = sync-only, no cleanup until manifest exists)
- Repo consolidation as multi-site: Merge developer-docs into this repo; each subdomain (team.symphonycore.com, dev.symphonycore.com) is a separate Docusaurus build + Cloudflare Pages project with independent access policies
- Multi-source configuration: Config-driven list of source repos (not just one hardcoded path)
- Multi-target publishing: Config-driven target site definitions with independent
publish_totag values routing content to specific site builds - Dry-run with diff reporting: Show what would be added, updated, and removed before committing changes
- CLI catalog preservation: Ensure manually-authored content in developer-docs is preserved during consolidation and not touched by sync cleanup (protected manual directories)
Out of Scope (Future Phases)
- README auto-sync from dev repos (Phase 2) -- configured list of repos whose READMEs publish to developer-docs
- Structured data publishing (Phase 2) -- YAML-to-markdown renderer with configurable field templates per target
- Partner portal (Phase 2) -- partner.symphonycore.com with Cloudflare Access invite-only auth
- Per-target content redaction (Phase 2) -- config-driven rules to strip marked sections for restricted-audience sites
- Customer-facing KB (Phase 3 / Research) -- help.symphonycore.com migration with SEO; significant unknowns remain
- CI/CD-triggered sync -- auto-sync on push to source repos via GitHub Actions
- Content versioning -- track and display document version history
- Cross-site search -- unified search across all published sites
- Content preview -- preview how a document will render on target site before publishing
Requirements
Outcome-Based Requirements
| ID | Desired Outcome | How We'll Measure Success | Priority | Status |
|---|---|---|---|---|
| OR-001 | Published sites accurately reflect current source state with no stale content | After sync: count of files on target site not present in source manifest = 0 | Must | Done |
| OR-002 | Single repository powers all internal documentation sites | Number of separate publishing repos = 1 | Must | Done |
| OR-003 | Content from multiple source repos can be published through one pipeline | Number of distinct source repos that successfully sync >= 2 | Must | Done |
| OR-004 | Content authors control publication targets via document-level tags | All published documents have matching publish_to tags in source; no untagged content appears on sites | Must | Done |
| OR-005 | Platform operator configures new sources and targets without code changes | Adding a new source or target requires only config file edits | Should | Done |
| OR-006 | Manually-authored content coexists with synced content without interference | CLI catalog pages persist across sync runs; no manual content is deleted or overwritten | Must | Done |
| OR-007 | Operator has clear visibility into sync changes before they happen | Dry-run output shows additions, updates, and removals with file paths | Should | Done |
| OR-008 | Dev repo READMEs are discoverable on developer documentation site | READMEs from configured repos appear on dev.symphonycore.com | Should | Done |
| OR-009 | Structured data (YAML/JSON) can be rendered as documentation pages | Client registry renders as a browsable client directory page | Could | Not Started |
| OR-010 | Audience-segmented publishing supports restricted-access portals | Partner-tagged content publishes to a separate site with invite-only Cloudflare Access | Could | Not Started |
| OR-011 | Customer-facing KB can be self-hosted with SEO benefits | Public-facing site at help.symphonycore.com with sitemap, meta tags, open graph | Won't (Research) | Not Started |
Functional Requirements
| ID | Requirement | Enables Outcome | Priority | Status | User Doc | Dev Doc |
|---|---|---|---|---|---|---|
| FR-001 | Maintain a sync manifest that records all files written by the sync tool, including file path, source hash, and sync timestamp | OR-001 | Must | Done | - | - |
| FR-002 | On each sync run, compare current manifest against previous manifest and remove files that are no longer in scope (archived, un-tagged, or deleted from source) | OR-001 | Must | Done | - | - |
| FR-003 | Persist the sync manifest to a known location in the target repo so it survives across sync runs | OR-001 | Must | Done | - | - |
| FR-004 | Support a configurable list of source repositories, each with its own path, content type, and scan settings | OR-003, OR-005 | Must | Done | - | - |
| FR-005 | Support a configurable list of target sites, each with its own docs path, Cloudflare Pages project, and accepted publish_to tag values | OR-004, OR-005 | Must | Done | - | - |
| FR-006 | Migrate developer-docs content (CLI catalog, synced docs, config) into the consolidated repo under a site-specific directory structure | OR-002, OR-006 | Must | Done | - | - |
| FR-007 | Support multiple Docusaurus site builds from a single repo, each deployable as a separate Cloudflare Pages project with its own subdomain and access policy | OR-002 | Must | Done | - | - |
| FR-008 | Designate directories as "manual" (not managed by sync tool) so cleanup never touches them | OR-006 | Must | Done | - | - |
| FR-009 | Provide a dry-run mode that outputs a diff summary: files to add, update, and remove, without making changes | OR-007 | Should | Done | - | - |
| FR-010 | Provide a --verbose mode that logs per-file transformation details, link rewrites, and warnings | OR-007 | Should | Done | - | - |
| FR-011 | Support a "readme" source adapter that scans a configured list of git repos and extracts README.md files for publishing | OR-008 | Should | Done | - | - |
| FR-012 | Support a "structured-data" source adapter that reads YAML/JSON files and renders them as markdown documentation pages using configurable templates | OR-009 | Could | Not Started | - | - |
| FR-013 | Support per-target Cloudflare Access policy configuration, including invite-only email lists for partner portals | OR-010 | Could | Not Started | - | - |
| FR-014 | Validate that all documents tagged with a publish_to target have that target defined in config; warn on unknown targets | OR-004 | Should | Done | - | - |
| FR-015 | Support incremental sync: skip files whose source hash has not changed since last sync | OR-001, OR-007 | Should | Done | - | - |
| FR-016 | Generate a post-sync summary report: documents added, updated, removed, failed, and warnings per target | OR-007 | Should | Done | - | - |
| FR-017 | Preserve existing content transformation pipeline (frontmatter rewriting, MDX escaping, Mermaid fixes, link rewriting, local path stripping) | OR-004 | Must | Done | - | - |
| FR-018 | Support per-source and per-target transformation overrides in config (e.g., different link rewrite rules per source repo) | OR-005 | Could | Not Started | - | - |
| FR-019 | Support per-target content redaction rules in config that strip sections between HTML comment markers (e.g., <!-- begin:internal-only --> ... <!-- end:internal-only -->) before publishing to restricted-audience sites | OR-010, OR-004 | Should | Done | - | - |
| FR-020 | Support configurable field templates for structured data rendering, allowing different field sets per target site (e.g., minimal for partner, full for internal) | OR-009 | Could | Not Started | - | - |
Non-Functional Requirements
| ID | Category | Requirement | Target | Enables Outcome | Status | Verified |
|---|---|---|---|---|---|---|
| NFR-001 | Performance | Full sync of all configured sources completes within a reasonable time for the current content volume | < 30 seconds for 200 documents | OR-001 | Done | Validated |
| NFR-002 | Reliability | Sync is idempotent -- running it multiple times with unchanged sources produces identical output | 100% deterministic output | OR-001 | Done | Validated |
| NFR-003 | Safety | Sync never modifies or deletes files outside of its managed directories | 0 accidental deletions of manual content | OR-006 | Done | Validated |
| NFR-004 | Portability | Sync tool runs on Windows (MSYS2/Git Bash), macOS, and Linux without platform-specific code | All platforms pass CI | OR-005 | Done | Windows verified |
| NFR-005 | Observability | Sync produces structured, parseable output (not just console.log) suitable for CI/CD integration | Machine-readable JSON output option | OR-007 | Done | Validated |
| NFR-006 | Security | No sensitive data (API keys, credentials, personal contact info) is published to any site | 0 credential leaks per audit | OR-004, OR-010 | Done | Validated |
| NFR-007 | Maintainability | Adding a new source adapter requires implementing a defined interface, not modifying core sync logic | Adapter interface documented; core untouched for new adapters | OR-005 | Done | Validated |
| NFR-008 | Backward Compatibility | Existing publish_to: [internal-wiki] and publish_to: [developer-docs] tags continue to work without modification to source documents | 100% backward compatibility | OR-004 | Done | Validated |
Requirements Summary
| Category | Total | Implemented | Partial | Not Started |
|---|---|---|---|---|
| Outcome Requirements (Must) | 6 | 6 | 0 | 0 |
| Outcome Requirements (Should) | 3 | 3 | 0 | 0 |
| Outcome Requirements (Could) | 2 | 0 | 0 | 2 |
| Functional (Must) | 8 | 8 | 0 | 0 |
| Functional (Should) | 7 | 7 | 0 | 0 |
| Functional (Could) | 5 | 0 | 0 | 5 |
| Non-Functional | 8 | 8 | 0 | 0 |
Traceability: Outcomes to Requirements
| Outcome ID | Outcome | Opportunity | Solution | Functional Reqs | NFRs |
|---|---|---|---|---|---|
| OR-001 | No stale content on published sites | OPP-001 | SOL-001 | FR-001, FR-002, FR-003, FR-015 | NFR-001, NFR-002 |
| OR-002 | Single publishing repository | OPP-002 | SOL-003 | FR-006, FR-007 | - |
| OR-003 | Multi-source publishing | OPP-003 | SOL-005 | FR-004 | NFR-004 |
| OR-004 | Tag-based publication control | OPP-001 | SOL-001 | FR-005, FR-014, FR-017 | NFR-006, NFR-008 |
| OR-005 | Config-driven extensibility | OPP-003, OPP-004 | SOL-005, SOL-006 | FR-004, FR-005, FR-018 | NFR-007 |
| OR-006 | Manual content preserved | OPP-002 | SOL-001 | FR-008 | NFR-003 |
| OR-007 | Sync visibility and reporting | OPP-001 | SOL-001 | FR-009, FR-010, FR-016 | NFR-005 |
| OR-008 | Dev READMEs on dev site | OPP-003 | SOL-005 | FR-011 | - |
| OR-009 | Structured data as docs | OPP-005 | SOL-007 | FR-012 | - |
| OR-010 | Partner portal | OPP-004 | SOL-006 | FR-013 | NFR-006 |
Orphaned Requirements: None -- all requirements trace to at least one outcome.
User Stories
Enhanced Format
-
As a documentation author, When I archive a document by moving it to the archive repo, I want to run sync and have the published copy automatically removed, So that readers never see outdated content, Which enables me to trust the published sites as the authoritative, current documentation, Measured by zero orphaned pages after any sync run.
-
As a platform operator, When I need to add a new documentation source (e.g., a new dev repo), I want to add an entry to the config file and run sync, So that content flows to the right target site without code changes, Which enables me to scale the publishing platform as the organization grows, Measured by time to onboard a new source < 5 minutes.
-
As a developer, When I update the README in my project repository, I want to have it appear on dev.symphonycore.com after the next sync, So that teammates can discover project documentation without visiting GitHub, Which enables me to keep documentation close to code while making it broadly accessible, Measured by README content matching source within one sync cycle.
-
As a platform operator, When I run a sync, I want to first preview what will change (additions, updates, removals), So that I can catch unexpected changes before they go live, Which enables me to maintain confidence in the publishing pipeline, Measured by dry-run output accurately predicting actual sync results.
-
As a business stakeholder, When I need to share confidential documentation with a partner, I want to tag the document with
publish_to: [partner-portal]and have it appear on a restricted site, So that partners access only what they should see via a controlled, professional portal, Which enables me to reduce ad-hoc document sharing and improve partner experience, Measured by partner documents accessible only to invited email addresses.
Success Metrics
Leading Indicators (Activity Metrics)
| Metric | What It Measures | Target | Linked Outcome |
|---|---|---|---|
| Sync runs per week | Pipeline usage frequency | >= 3 runs/week | OR-001 |
| Source repos configured | Platform adoption breadth | >= 2 in MVP, >= 4 in Phase 2 | OR-003 |
Documents with publish_to tags | Author adoption of tagging | >= 80% of publishable docs tagged | OR-004 |
| Dry-run usage rate | Operator confidence in preview | >= 50% of sync runs preceded by dry-run | OR-007 |
Lagging Indicators (Outcome Metrics)
| Metric | What It Measures | Current Baseline | Target | Timeline | Business Impact |
|---|---|---|---|---|---|
| Orphaned pages on published sites | Stale content elimination | Unknown (no audit yet) | 0 | MVP launch | Readers trust published content |
| Publishing repos maintained | Infrastructure consolidation | 2 (internal-docs + developer-docs) | 1 | MVP launch | 50% reduction in maintenance |
| Time to add new source | Platform extensibility | N/A (code change required) | < 5 min (config only) | MVP launch | Faster content onboarding |
| Time to add new target site | Platform extensibility | N/A (new repo required) | < 30 min (config + CF Pages setup) | Phase 2 | Faster audience expansion |
North Star Metric
Primary metric: Percentage of published pages that match their source document (content accuracy rate) Target: 100% after every sync run Why this metric: The core value of a publishing platform is that what readers see matches what authors wrote -- no stale content, no missing content, no transformation errors.
Timeline & Milestones
| Milestone | Description | Target Date |
|---|---|---|
| MVP Sprint 1 | Manifest-based sync with stale content cleanup; multi-source config; dry-run with diff | TBD |
| MVP Sprint 2 | Repo consolidation: merge developer-docs into internal-docs; single Docusaurus instance | TBD |
| Phase 2 Sprint 1 | README source adapter; structured data (YAML) renderer | TBD |
| Phase 2 Sprint 2 | Partner portal (partner.symphonycore.com) with Cloudflare Access invite-only auth | TBD |
| Phase 3 | Research: customer-facing KB (help.symphonycore.com) migration and SEO strategy | TBD |
Dependencies
| Dependency | Type | Impact | Status |
|---|---|---|---|
| Cloudflare Pages project for consolidated site | Technical | Must configure CF Pages to serve consolidated content | Existing (team.symphonycore.com) |
| Cloudflare Access policies for partner portal | Technical | Need invite-only policy for partner.symphonycore.com | Not started |
| symphony-core-documents repo | External | Primary content source; no changes needed to source docs | Available |
| Developer repo access | External | Need read access to configured dev repos for README sync | Available (GitHub org) |
| Client registry YAML | External | Source data for structured data publishing | Available (symphony-flow repo) |
| DNS configuration for partner.symphonycore.com | Technical | New subdomain needed for partner portal | Not started |
Constraints
| Constraint | Type | Description |
|---|---|---|
| Cloudflare Free Tier | Budget | 50-user cap on Cloudflare Access; login page template-based only (no custom HTML) |
| Windows compatibility | Technical | Tool must work on Windows (MSYS2/Git Bash); ASCII-safe output only |
| Existing tag format | Technical | publish_to: [internal-wiki, developer-docs] format must remain backward compatible |
| No CI/CD in MVP | Technical | Sync is manually triggered; automated CI/CD triggers are deferred |
| Single operator | Resource | Platform operator is a single person; tool must be low-maintenance |
| Client registry sensitivity | Security | Client contact info (emails, phones) must be redacted or excluded from published pages |
Risks & Mitigations
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Consolidation breaks existing URLs on team.symphonycore.com or dev.symphonycore.com | Medium | High | Map existing URL structures before consolidation; implement redirects if needed |
| Manifest corruption causes mass deletion of published content | Low | High | FR-009 dry-run gate before destructive sync; backup manifest before overwriting |
| CLI catalog pages accidentally deleted by sync cleanup | Medium | High | FR-008 designates manual directories as protected; NFR-003 enforces boundary |
| Cloudflare 50-user cap blocks partner portal scaling | Low | Medium | Monitor usage; evaluate paid tier if cap is reached |
| Source repo structure changes break sync routing | Medium | Medium | FR-014 validates tags against config; warnings on unknown targets |
| Structured data contains sensitive info that gets published | Low | High | NFR-006 mandates redaction rules; FR-012 template controls which fields render |
Related Documents
| Document | Type | Relationship | Link |
|---|---|---|---|
| Auth Evolution PRD | PRD | Related (Cloudflare Access setup) | prd-auth-evolution.md |
| Deploy Status PRD | PRD | Related (deployment monitoring) | prd-deploy-status.md |
| Sync Tool Source | Code | Current implementation being replaced | sync-tool/src/ |
| Developer Docs Repo | Code | Consolidation source | C:\Users\Rohit\workspace\Work\software\developer-docs |
| Client Registry | Data | Structured data source | C:\Users\Rohit\workspace\Work\software\symphony-flow\_outbox\client-registry.yaml |
Glossary
| Term | Definition |
|---|---|
| Sync manifest | A JSON file tracking all files written by the sync tool, including source hash, target path, and timestamp |
| Source adapter | A pluggable module that knows how to scan a specific type of source (markdown repo, README files, YAML data) and produce documents for the sync pipeline |
| Target site | A Docusaurus-powered website deployed to a specific subdomain with its own Cloudflare Access policy |
| Orphaned page | A published page whose source document no longer exists or no longer targets that site |
| Manual directory | A directory in the target repo that is excluded from sync cleanup because it contains manually-authored content |
| Publish tag | The publish_to YAML frontmatter field that controls which target sites receive a document |
Resolved Decisions
| # | Question | Decision | Rationale |
|---|---|---|---|
| D-001 | URL structure for consolidated site | Docusaurus multi-instance with path prefixes (/team/*, /dev/*) | Clear audience separation; easier to map Cloudflare Access policies per-path if needed |
| D-002 | Sync manifest storage | Committed to repo (version-controlled) | Anyone who clones can sync with accurate state; git history shows what changed when |
| D-003 | Partner content model | Filtered subset -- authors add publish_to: [partner-portal] alongside other tags | Same doc can appear on internal + partner sites; simpler authoring |
| D-004 | Client registry rendering | Configurable per-field via templates | Different templates for different targets (minimal for partner, full for internal) |
| D-005 | dev.symphonycore.com handling | Keep as separate subdomain with separate Cloudflare Access policy, served from same repo | Different people have access to dev vs team; consolidate code, not access |
| D-006 | Primary domain | Keep team.symphonycore.com | No DNS disruption; familiar to users |
| D-007 | First sync run (no manifest) | Sync only, no cleanup | Safe default -- creates manifest without risking deletions; cleanup kicks in from second run onward |
| D-008 | Per-target content transformation | Config-driven redaction rules (e.g., strip <!-- internal-only --> sections for partner) | Keeps author experience simple; global rules in config avoid per-doc complexity |
Architecture: Multi-Subdomain from Single Repo
A key architectural pattern emerges from D-005: one repo, multiple Cloudflare Pages deployments, each with its own subdomain and access policy.
internal-docs/ (single repo)
├── sites/
│ ├── team/ # team.symphonycore.com -- Docusaurus instance
│ ├── dev/ # dev.symphonycore.com -- Docusaurus instance
│ └── partner/ # partner.symphonycore.com -- Docusaurus instance (Phase 2)
├── docs/
│ ├── synced/ # Content from sync pipeline
│ ├── cli/ # Migrated from developer-docs (manual)
│ └── ...
└── sync-tool/ # Unified publishing pipeline
Each subdomain is a separate Cloudflare Pages project pointing to the same repo but with different build commands/output directories and independent Cloudflare Access policies. This enables:
- Shared sync tool and content pipeline
- Per-subdomain access control (team = @symphonycore.com OTP, dev = approved dev emails, partner = invite-only)
- Content routing via
publish_totags to specific site builds
Resolved Decisions (Continued)
| # | Question | Decision | Rationale |
|---|---|---|---|
| D-009 | Non-Docusaurus target sites | Docusaurus only | Keeps pipeline simple -- one framework, one build system. If help.symphonycore.com needs a different framework, it gets a separate pipeline. |
| D-010 | Role-based content visibility within a single site | Future consideration (Phase 3+) | Subdomain-level access via Cloudflare Access is sufficient for now. Some content (financial, HR) may need restricted visibility later. |
| D-011 | Cloudflare Pages multi-site build strategy | Separate CF Pages projects per subdomain | Each project has its own build command (npm run build:team, npm run build:dev), output directory, and independent Cloudflare Access policy. |
| D-012 | Per-target redaction marker format | HTML comments (<!-- begin:internal-only --> ... <!-- end:internal-only -->) | Invisible in rendered markdown, familiar syntax, easy to parse. Not coupled to MDX/Docusaurus. |
Open Questions
All major architectural questions have been resolved. The following are minor implementation details to be decided during sprint planning:
- Should the sync manifest include content checksums (SHA-256) or rely on file modification timestamps for change detection?
- What naming convention for CF Pages build scripts? (
build:team,build:dev,build:partner?)
Appendix
A. Litmus Test Findings
| Req ID | Original (Solution-Prescriptive) | Rewritten (Solution-Neutral) | Rationale |
|---|---|---|---|
| FR-001 | Store sync manifest as a JSON file in .sync-manifest.json | Maintain a sync manifest that records all files written by the sync tool | Original prescribed JSON format and file name; implementation should decide storage format |
| FR-007 | Use Docusaurus multi-instance plugin to serve team and dev docs | Configure Docusaurus to serve multiple site sections from a single instance | Original prescribed a specific Docusaurus plugin; multiple valid approaches exist |
| FR-011 | Clone configured repos and extract README.md files | Support a "readme" source adapter that scans configured repos and extracts README files | Original prescribed git clone; could also use GitHub API, sparse checkout, or local paths |
B. Current Sync Tool Architecture (for reference)
symphony-core-documents/
|
v (scan for publish_to tags)
sync-tool/
├── scanner.ts -- glob *.md, parse frontmatter, filter by publish_to
├── router.ts -- compute destination paths, strip number prefixes
├── transformer.ts -- frontmatter rewrite, MDX escape, Mermaid fix, link rewrite
├── publisher.ts -- mkdir + writeFile (NO deletion logic)
└── cli.ts -- commander CLI, config loading, orchestration
|
v (write files)
docs/synced/ (internal-wiki target)
../developer-docs/docs/ (developer-docs target, cross-repo)
C. Developer-Docs Repo Analysis
Recommendation: Merge into internal-docs.
| Aspect | Finding |
|---|---|
| Framework | Same (Docusaurus 3.8.1 vs 3.9.2) |
| Deployment | Same (Cloudflare Pages) |
| Auth | Same (Cloudflare Access OTP) |
| Search | Same (@easyops-cn/docusaurus-search-local) |
| Sync tool | None -- depends on internal-docs |
| Unique content | CLI Command Catalog (48 pages in docs/cli/) |
| Synced content | Architecture, reference, sprints from symphony-core-documents |
The CLI catalog is the only unique, manually-authored content. It can be migrated to docs/cli/ in the consolidated repo. All synced content will be handled by the unified pipeline. Docusaurus version should be aligned to 3.9.2.
D. Suggested Value-Added Features (Beyond Core Requirements)
These features were identified during discovery as potential enhancements that would add significant value to a content publishing platform of this nature:
- Content health dashboard: A generated page showing sync status, document count per target, last sync timestamp, and any warnings -- gives operators at-a-glance platform health
- Broken link detection: Post-sync validation that checks all internal links resolve to actual pages; report broken links before deployment
- Document dependency graph: Visualize which documents reference each other across sites; identify impact when a document is archived
- Content freshness indicators: Display "last updated" badges on published pages; flag documents not updated in > 90 days
- Automated sync via GitHub Actions: Trigger sync on push to any configured source repo; create PR with changes for review before merge
- Content search across all sites: Unified search index spanning team, dev, and partner sites (respecting access controls)
- Diff preview in PR: When sync creates a PR, include a human-readable diff of content changes (not just file-level)
- Publish audit log: Persistent log of all sync operations -- who ran it, what changed, when -- for compliance and debugging
- Template-based rendering for non-markdown sources: Extensible template system for rendering YAML, JSON, CSV, or API responses as documentation pages
- Content approval workflow: Optional review gate where tagged documents require approval before publishing to restricted sites (e.g., partner portal)
E. References
- Docusaurus Multi-instance Docs
- Cloudflare Access Application Policies
- Cloudflare Pages Custom Domains
End of Document