惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MongoDB | Blog
MongoDB | Blog
AI
AI
B
Blog RSS Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Threatpost
I
Intezer
P
Proofpoint News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
S
Schneier on Security
Webroot Blog
Webroot Blog
Recorded Future
Recorded Future
aimingoo的专栏
aimingoo的专栏
L
Lohrmann on Cybersecurity
Simon Willison's Weblog
Simon Willison's Weblog
MyScale Blog
MyScale Blog
Project Zero
Project Zero
L
LangChain Blog
B
Blog
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
F
Fortinet All Blogs
美团技术团队
Engineering at Meta
Engineering at Meta
Cisco Talos Blog
Cisco Talos Blog
D
Docker
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
S
Security Affairs
Attack and Defense Labs
Attack and Defense Labs
N
News | PayPal Newsroom
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
W
WeLiveSecurity
V2EX - 技术
V2EX - 技术
TaoSecurity Blog
TaoSecurity Blog
博客园 - Franky
P
Proofpoint News Feed
Jina AI
Jina AI
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
雷峰网
雷峰网
The Hacker News
The Hacker News
G
GRAHAM CLULEY

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
What is MonoSpecs: Why It's a Further Upgrade and Extension of OpenSpec
Hagicode · 2026-06-20 · via DEV Community

What is MonoSpecs: Why It's a Further Upgrade and Extension of OpenSpec

When a product system expands to 40+ independent Git repositories, where should "specifications" be placed? This article discusses the two steps HagiCode took in multi-repository governance: first lifting OpenSpec to the main repository, then developing MonoSpecs as a multi-repository management solution on top of it. Really nothing special, just stumbled through some pitfalls and wanted to document them.

Background

Anyone who's worked on a slightly larger product has probably had this experience—initially the code was in a single repository, well-organized and peaceful; later the frontend, backend, desktop app, documentation site, official website, and build tools each became independent repositories, and the number of repositories kept growing like wild grass, unstoppable. Then when you wanted to write a "specification document" for some cross-repository functionality, suddenly you didn't know where to write it—it's a bit like childhood pocket money, suddenly gone without a trace.

Our own HagiCode is such a product system composed of 40+ independent Git repositories. In the early days, we directly stuffed OpenSpec's openspec/ directory into the backend sub-repository hagicode-core, thinking that since the backend is the core, placing it there would be most stable. As the number of repositories grew, this approach exposed a series of headache-inducing problems. After all, the code world never actually becomes stable just because you "thought it would be stable."

The first pain point: specs are trapped in a single sub-repository. If a functionality affects both the frontend web and the backend hagicode-core, I have to write proposals in hagicode-core, then go to other sub-repositories to execute code changes. Which repository a proposal belongs to itself becomes a controversy.

The second pain point: sub-repositories are not pure. Each sub-repository carries its own openspec/, mixing specification documents with product code. Someone clones your frontend repository and ends up bringing back a bunch of backend proposal documents, completely bewildered.

The third pain point: AI Agents struggle to understand repository relationships. Each sub-repository is independent, with no machine-readable "manifest" telling the AI: what repositories this product consists of, what each is responsible for, which is editable, which is read-only reference.

The fourth pain point: high cost of cross-repository editing. To modify a spec, you must first cd into the corresponding sub-module, paths jump around, creating significant cognitive burden for collaboration.

Against this background, we first performed an "OpenSpec Monorepo Migration," lifting specs from sub-repositories to the monorepo root directory. Then on top of it, we developed MonoSpecs as a multi-repository management solution. Understanding the progressive relationship between these two steps is the key to understanding "why MonoSpecs is a further upgrade and extension of OpenSpec."

About HagiCode

The solution shared in this article comes from our practical experience in the HagiCode project. HagiCode is an AI code assistant project with many repositories and frequent cross-language collaboration. This structural complexity forced us to make both "specifications" and "repository governance" solid. The MonoSpecs solution was gradually polished through multi-repository practice—no brilliant strokes, just a few more steps taken.

OpenSpec Solves "How Specifications Are Written and Evolved"

To clarify the relationship between the two, we need to first look at what each is responsible for.

OpenSpec is essentially a spec-driven change management workflow. Its core artifacts look like this:

openspec/
├── specs/          # Currently active capability specifications (one spec.md per capability)
├── changes/        # In-progress proposals
│   └── archive/    # Archived historical proposals
└── project.md

It answers the question: a change goes through a lifecycle of proposal, design, tasks, archive, and merges deltas into specs when archiving. This mechanism itself is unrelated to "how many repositories there are, where they are, who manages them"—it only cares about how spec files are organized.

Through a migration proposal, we lifted 82+ spec files originally scattered in hagicode-core/openspec/ to the monorepo root's openspec/, making all specs visible in one place with unified version control.

But this migration was essentially just "moving spec files," and didn't answer the more fundamental question: what sub-repositories does this monorepo actually consist of? What are the relationships between these sub-repositories? This is what MonoSpecs fills in.

MonoSpecs Solves "How Multi-Repositories Themselves Are Managed"

The core of MonoSpecs is a machine-readable manifest file: .hagicode/monospecs.yaml. It does four things that OpenSpec doesn't address at all.

The first: declare sub-repository manifest. Each repository's path, url, displayName, icon, tags, whether collapsed to "More", all written in a single YAML, clear at a glance.

The second: drive clone scripts. scripts/clone-repos.mjs reads this YAML directly, batch git clone, no longer hardcoding repository lists. Adding a new repository just requires one line in the YAML, the script needs zero changes.

The third: provide project structure context for AI/IDE. Paired with AGENTS.md, AI Agents can instantly see which repository is editable, which is reference-only, and what the tech stack is.

The fourth: anchor OpenSpec artifacts to the main repository. Specs are no longer scattered across sub-repositories, but unified in the main repository root's openspec/, keeping sub-repositories pure.

Two Layers of Meaning, Don't Get Confused

In MonoSpecs' official guide, a very confusing point is clearly pointed out: MonoSpecs actually has two layers of meaning.

One layer is the configuration system layer, referring to the .hagicode/monospecs.yaml configuration file itself, along with its accompanying loading, validation, and caching mechanisms.

The other layer is the repository type layer, referring to a "main repository + multiple sub-repositories + centralized specs" repository organization pattern. When we say a project "is a MonoSpecs project," it means it adopts this structure.

These two layers叠加 together form the complete MonoSpecs. Many people encountering it for the first time only see the YAML file layer, thinking MonoSpecs is just a configuration manifest, but its value is actually more in the second layer—a clear multi-repository collaboration paradigm. Beautiful things often aren't visible at first glance; you need to look a few more times.

Why "Upgrade and Extension"

Putting them side by side, the relationship becomes clear:

Dimension OpenSpec MonoSpecs
Focus spec file content and lifecycle repository organization structure and manifest
Core artifact openspec/specs/*/spec.md .hagicode/monospecs.yaml
Depends on the other Doesn't depend on MonoSpecs Depends on OpenSpec, reuses its openspec/ for change management
Pain point solved How specifications are written and evolved How multi-repositories are declared, cloned, and understood by AI
Scope Can be used in any repository Designed specifically for "one main multiple sub" multi-repository structure

Fundamentally, MonoSpecs doesn't replace OpenSpec, but adds a layer of "repository governance" on top of it. Using monospecs.yaml to describe repository topology, using centralized openspec/ to decouple specs from sub-repositories, using commit_when_archive to automatically archive commits to the main repository.

If using an analogy: OpenSpec provides "change syntax," MonoSpecs provides "multi-repository semantics." The former is the premise of the latter, the latter is an extension of the former. All roads lead to Rome, just this time, the road is a bit longer than imagined.

How to Implement: Four Steps

Step 1: Establish Main Repository and Configuration File

Place the configuration file in the monorepo root directory, declaring all sub-repositories. Taking our own project as an example, the structure is roughly like this:

# .hagicode/monospecs.yaml
version: "1.0"
commit_when_archive: true

repositories:
- path: "repos/web"
  url: "https://github.com/HagiCode-org/web.git"
  displayName: "Frontend"
  tags: [frontend, react, pcode-client]

- path: "repos/hagicode-core"
  url: "https://github.com/newbe36524/pcode"
  displayName: "Backend"
  tags: [backend, dotnet, orleans]

- path: "repos/docs"
  url: "https://github.com/HagiCode-org/docs.git"
  displayName: "Docs"
  tags: [docs, astro, starlight]
  ui:
    collapseToMore: true   # Collapse to "More" in the UI

There are a few fields to pay special attention to:

  • path is the local path relative to the main repository root, and is also the unique key for each record.
  • url is the Git remote address, which the clone script relies on to pull code.
  • displayName / icon / tags only affect UI display and AI context, not clone behavior.
  • commit_when_archive: true automatically commits to the main repository when OpenSpec proposals are archived.

Step 2: Lift OpenSpec to Main Repository Root Directory

Before and after migration comparison:

Before migration (specs trapped in sub-repository)         After migration (specs centralized in main repository)
hagicode-core/                                            .  (main repository root)
└── openspec/                                             ├── .hagicode/monospecs.yaml
    └── specs/  (82+ specs)                               ├── openspec/
                                                          │   ├── specs/   (centralized management)
                                                          │   └── changes/
                                                          └── repos/
                                                              ├── hagicode-core/  (pure, no openspec)
                                                              ├── web/
                                                              └── docs/

From then on, sub-repositories no longer carry openspec/, and the main repository becomes the sole source of truth for specs. This step looks simple, but the benefits it brings are very practical—any engineer standing at the main repository root can see all specifications for the entire product system.

Step 3: Make Clone Scripts Read Configuration Instead of Hardcoding

The core logic of scripts/clone-repos.mjs is to read YAML and clone one by one:

const CONFIG_PATH = path.join(__dirname, '..', '.hagicode', 'monospecs.yaml');
// Parse repositories array
// Execute git clone <url> <path> for each entry
// Skip or git pull if target directory already exists

When adding a new repository, you only need to add one line in the YAML, without touching the script. This small change saves countless "forgot to sync repository list" disputes. After all, who wants to repeat labor?

Step 4: Backend Provides Unified MonoSpecs Service Layer

If you don't extract an abstraction layer, configuration parsing logic can easily scatter across GitAppService, ProjectAppService and other corners. HagiCode extracted IMonoSpecsService in the ClaudeHelper module, exposing a set of clear capabilities:

public interface IMonoSpecsService
{
    Task<MonoSpecsConfigDto> GetConfigAsync(string projectPath);
    Task<List<RepositoryInfoDto>> GetSubRepositoriesAsync(string projectPath);
    Task<MonoSpecsDataDto> GetMonoSpecsDataAsync(string projectPath);
    Task<MonoSpecsManagementDto> GetManagementDocumentAsync(string projectPath);
    Task<MonoSpecsManagementDto> InitializeManagementDocumentAsync(string projectPath);
    Task ValidateManagementDocumentAsync(string projectPath, UpdateMonoSpecsManagementRequestDto request);
    Task SaveManagementDocumentAsync(string projectPath, UpdateMonoSpecsManagementRequestDto request);
}

This service is responsible for loading, validating, caching configuration, and providing "initialize minimal template" capability—one-click generation of monospecs.yaml, repos/, openspec/changes/archive/, openspec/specs/ skeleton for an empty project, and automatically completing .gitignore. Caching, like memory, once remembered, next time you don't have to think hard.

Several Pitfalls in Practice

Initializing a Brand New MonoSpecs Project

After calling InitializeManagementDocumentAsync, this structure will appear on disk:

my-project/
├── .gitignore               # Added repos/ ignore rules (idempotent, won't duplicate append)
├── .hagicode/
│   └── monospecs.yaml       # Minimal template: version / commit_when_archive / repositories: []
├── openspec/
│   ├── changes/archive/
│   └── specs/
└── repos/                   # Empty directory, waiting for clone

There are a few boundaries to pay attention to here, all extracted from specs:

  • Idempotent: Existing repos/, openspec/ directories will be preserved and won't error.
  • No overwrite: If monospecs.yaml already exists and can be parsed normally, initialization won't touch it, only supplements missing .gitignore rules and openspec directories.
  • Reject dirty config: Existing but unparsable monospecs.yaml will be directly rejected, returning diagnosable error information, absolutely no overwrite.
  • No auto-scan: Initialization won't scan disk directories into repository entries on its own, repositories defaults to empty, requiring manual or UI filling.

Configuration File Location Migration Trap

Historically, monospecs.yaml was once placed in the project root, later forcibly migrated to .hagicode/monospecs.yaml. This is clearly stated in the spec:

Root directory's monospecs.yaml is no longer detected, nor as a compatibility fallback. Clone scripts only recognize .hagicode/monospecs.yaml.

So when upgrading old projects, you must manually execute mv monospecs.yaml .hagicode/monospecs.yaml, with no silent compatibility path. At first glance it seems unreasonable, but thinking carefully, this is to completely eliminate the ambiguity of "both locations might take effect"—once this ambiguity exists, troubleshooting can drive people crazy. After all, who wants to look for answers between two files?

Save Validation: Don't Write Invalid Configuration

Before writing back through SaveManagementDocumentAsync, the service performs field-level validation. Several typical rejection scenarios:

  • Two repository entries with duplicate path → reject, return conflicting fields.
  • Any entry missing path → reject, return required field error.
  • url is non-empty but not a valid absolute URL → reject.

Only after validation passes will it serialize to YAML and write to disk, while invalidating that project path's configuration cache, ensuring the next read gets the latest content. This step looks trivial, but can avoid countless "why didn't my configuration changes take effect" tickets. After all, when these tickets accumulate, who can bear it?

Workspace Mode vs Manual Repositories Mode

The configuration file supports two ways to derive repository lists.

One is manual repositories mode, directly listing each repository in the YAML, management document marked as editable.

The other is workspace mode, declaring a .code-workspace file, which derives the repository list. In this mode the management document is marked as read-only, prohibiting direct modification of the repository array, only modifying supported top-level fields.

Our own HagiCode Mono currently has workspace mode commented out, using manual mode. The reason is simple: manual mode allows fine-grained control of each repository's icon and tags, making UI display effects more controllable. As they say, things you can control always make you feel more at ease.

Practical Advice for AI Agents

Now with AI programming becoming increasingly popular, the MonoSpecs solution actually has an implicit value: it provides AI with a structured project map.

In multi-repository collaboration, AGENTS.md and monospecs.yaml are two key contexts for AI. The recommended workflow is:

  1. First read monospecs.yaml to get repository topology, understanding who is editable, who is reference-only.
  2. Then read root AGENTS.md's "Active Edit Scope" to confirm the currently allowed modification scope.
  3. Cross-repository changes uniformly write proposals in the main repository root's openspec/changes/, don't start separate openspec in each sub-repository.

This convention lets AI stably understand the division of "main repository manages specs, sub-repositories manage code," and won't mistakenly write specs into sub-repositories—we've stumbled into this pit several times before. Actually can't blame AI, after all sub-repositories and main repositories look so similar, who can tell at a glance?

Summary

One sentence summary: OpenSpec defines "how changes are written," MonoSpecs defines "how repositories are placed."

The former is the grammatical foundation of the latter, the latter extends the former from single-repository context to multi-repository context, and converges repository topology, clone process, AI context, and specs ownership in one go with a YAML manifest. This is the true meaning of "MonoSpecs is a further upgrade and extension of OpenSpec"—not replacement, but adding a layer of multi-repository semantics on top of it.

If you're also working on a multi-repository product of similar scale, consider whether these two layers are both laid out properly. No matter how beautifully specifications are written, without clear repository governance supporting them, eventually it will all become a chaotic mess......

References

Summary

Surrounding "What is MonoSpecs: Why It's a Further Upgrade and Extension of OpenSpec," a more prudent advancement approach is to first gradually run through key configurations, dependency boundaries, and implementation paths, then supplement optimization details.

Once goals, steps, and acceptance criteria are clear, such solutions can usually more smoothly enter actual delivery.

Original Article & License

Thanks for reading. If this article helped, consider liking, bookmarking, or sharing it.
This article was created with AI assistance and reviewed by the author before publication.