You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I propose adding an extensible Hook capability to OpenSpec's /opsx:archive supportcommand. This would allow us to inject custom consolidation logic when a specification (spec) is archived, meeting project-specific post-processing requirements.
📜 Context & Motivation
Currently, OpenSpec's archiving operation might be a relatively closed process. However, in practical use, we often need to perform deeper consolidation work based on archived changes.
A typical scenario is Error Log Management:
OpenSpec may generate an "error log" recording issues during Schema definition and execution. When certain problems are fixed by modifying the Schema, we hope that during the archiving of these changes, we can:
Automatically Identify: Associate the "errors" fixed by this archival.
Update Status: Move these "errors" from the "to-be-fixed" list to the "fixed" historical archive.
Organize Metadata: Perhaps also tag the fixed errors with version, archiving time, etc.
Without a Hook mechanism, this consolidation work requires manual effort or relies on external scripts that are difficult to synchronize atomically with the archiving process, making errors or omissions likely.
💡 Proposed Solution
I suggest referencing common CI/CD or Git Hook designs, reserving the ability to execute user-defined scripts at key nodes in the archiving workflow.
Hook Types:
pre-archiveHook: Triggered beforethe archive operation executes. Can be used for validation, data preparation, or preliminary consolidation.
post-archiveHook: Triggered afterthe archive operation completes. Ideal for follow-up actions like updating the error log, generating reports, sending notifications, etc.
Configuration Method:
The path to the Hook scripts could be specified in OpenSpec's configuration file (e.g., config.yaml).
The enrichment comment has been posted on issue #682. Here's a summary of what was included:
Existing infrastructure: OpenSpec uses Commander.js preAction/postAction hooks internally for telemetry and validation, but has no user-extensible hook/plugin system.
Archive code analysis: Identified the exact integration points in src/core/archive.ts where pre-archiveHook and post-archiveHook would fit within the 4-step archive workflow (validation → task check → spec updates → directory move).
Configuration path: The ProjectConfigSchema in src/core/project-config.ts would need to be extended with hook fields, with Zod validation.
Related issues: Linked 6 related issues — #328 (Claude Hooks), #331 (auto-update project.md on archive), #369 (post-archive actions), #641 (error correction log), plus archive bugs #412 and #498.
Implementation considerations: Highlighted 4 key challenges — no existing subprocess spawning infrastructure, lack of atomicity/rollback in the current archive flow, error propagation contract design, and cross-platform script execution needs.
Thanks for the detailed feature request. Here's an analysis based on the current codebase to help developers get started.
Existing Hook Infrastructure
OpenSpec already uses Commander.js .hook() for CLI lifecycle management, but these are internal framework hooks — not user-extensible:
Global preAction/postAction hooks in src/cli/index.ts:71-88 — used for telemetry and NO_COLOR setup
Command-specific preAction hooks — used for validation (src/commands/config.ts:31-37), deprecation warnings (src/cli/index.ts:210-212, src/commands/spec.ts:117-119), and experimental notices (src/commands/schema.ts:296-298)
There is no existing user-extensible hook/plugin system, no EventEmitter pattern, and no middleware chain. Extensibility today is configuration-driven (openspec/config.yaml) rather than code-driven.
Archive Command — Key Integration Points
The archive implementation lives in src/core/archive.ts (340 lines), specifically the ArchiveCommand.execute() method at line 51. The current workflow is:
Validation (lines 92-151) — validates proposal and delta specs
Task progress check (lines 174-194) — warns about incomplete tasks
Spec updates (lines 196-265) — finds and applies delta specs to main specs
Directory move (lines 267-287) — moves change to openspec/changes/archive/<date>-<name>/
A pre-archiveHook would logically fit between validation (step 1) and the spec update/move operations. A post-archiveHook would fit after step 4 (line 287), once the directory has been moved.
Configuration Approach
The project config is loaded in src/core/project-config.ts:66-161 from openspec/config.yaml (or .yml), validated via Zod (ProjectConfigSchema at line 19). The current schema supports:
Adding hook configuration would require extending ProjectConfigSchema with new fields (e.g., hooks.pre-archive, hooks.post-archive pointing to script paths) and adding validation logic.
Related Issues
Several existing issues describe use cases that a hook system would address:
Script execution model: The codebase is pure TypeScript/ESM with no existing subprocess spawning for user scripts. A hook runner would need to be built from scratch — likely using Node.js child_process.execFile or spawn to run user-defined scripts.
Atomicity concern (raised in the issue): The current archive operation is not transactional — spec updates (step 3) and the directory move (step 4) happen sequentially. If a post-archiveHook fails, rollback logic would need to be added, which doesn't exist today.
Error propagation: The execute() method currently returns void and communicates via console.log / thrown errors. Hooks would need a defined contract for success/failure signaling (exit codes, stdout parsing, etc.).
Cross-platform: The archive already handles Windows-specific issues (EPERM/EXDEV fallbacks at lines 36-48). Any hook execution path would also need Windows compatibility (e.g., .cmd/.ps1 scripts alongside shell scripts).
This is closely related to the discussion in #557 — specifically this comment where I proposed lifecycle hooks as LLM instructions defined in schema.yaml, rather than shell scripts.
The core idea: instead of spawning subprocesses, hooks are prompts that the LLM follows at lifecycle points (post-archive, post-sync, etc.):
hooks:
post-archive:
instruction: | Review the archived change and cross-reference with docs/errors/pending.md. For each error that was fixed by this change: 1. Move the entry from docs/errors/pending.md to docs/errors/resolved.md 2. Tag it with the change name, resolution date, and affected specs 3. If the fix involved a schema modification, note which fields changed Summarize what was consolidated at the end.
This covers the error log consolidation scenario described here — without needing subprocess infrastructure, rollback mechanics, or cross-platform script handling.
For cases where deterministic execution is needed (notifications, CI triggers, external API calls), hooks could also support a run field:
hooks:
post-archive:
instruction: | Review the archived change and consolidate the error log...run: ./scripts/notify-slack.sh
This way instruction handles the LLM-driven work (reasoning over artifacts, updating docs) and run handles the programmatic side (sending a webhook, updating a database). Both are optional — use one, the other, or both.
Hooks at the schema level make sense for behavior that's inherent to the workflow — e.g., an ADR schema that always generates decision records on archive. But project-specific hooks (notify a particular Slack channel, update a project-specific error log, run a script that only exists in your repo) belong in the project config (openspec/config.yaml). The same mechanism could apply at both levels — schema defines default hooks for the workflow, config adds or overrides with project-specific ones.
The only core change required is reading hooks from both schema.yaml and config.yaml, and surfacing them at the right moment. Everything else is defined by each schema or project, keeping the core minimal.
This is closely related to the discussion in #557 — specifically this comment where I proposed lifecycle hooks as LLM instructions defined in schema.yaml, rather than shell scripts.这与 #557 中的讨论密切相关——特别是在这条评论中,我建议将生命周期挂钩作为schema.yaml中定义的 LLM 指令,而不是 shell 脚本。
The core idea: instead of spawning subprocesses, hooks are prompts that the LLM follows at lifecycle points (post-archive, post-sync, etc.):核心思想:挂钩不是生成子流程,而是 LLM 在生命周期点(归档后、同步后等)遵循的提示:
hooks:
post-archive:
instruction: |
Review the archived change and cross-reference with docs/errors/pending.md.
For each error that was fixed by this change:
1. Move the entry from docs/errors/pending.md to docs/errors/resolved.md
2. Tag it with the change name, resolution date, and affected specs
3. If the fix involved a schema modification, note which fields changed
Summarize what was consolidated at the end.
This covers the error log consolidation scenario described here — without needing subprocess infrastructure, rollback mechanics, or cross-platform script handling.这涵盖了此处描述的错误日志整合场景 - 无需子流程基础设施、回滚机制或跨平台脚本处理。
For cases where deterministic execution is needed (notifications, CI triggers, external API calls), hooks could also support a run field:对于需要确定性执行的情况(通知、CI 触发器、外部 API 调用),挂钩还可以支持run字段:
hooks:
post-archive:
instruction: |
Review the archived change and consolidate the error log...
run: ./scripts/notify-slack.sh
This way instruction handles the LLM-driven work (reasoning over artifacts, updating docs) and run handles the programmatic side (sending a webhook, updating a database). Both are optional — use one, the other, or both.通过这种方式, instruction处理 LLM 驱动的工作(对工件进行推理、更新文档),而run处理编程方面(发送 Webhook、更新数据库)。两者都是可选的——使用一个、另一个或两者都使用。
Hooks at the schema level make sense for behavior that's inherent to the workflow — e.g., an ADR schema that always generates decision records on archive. But project-specific hooks (notify a particular Slack channel, update a project-specific error log, run a script that only exists in your repo) belong in the project config (openspec/config.yaml). The same mechanism could apply at both levels — schema defines default hooks for the workflow, config adds or overrides with project-specific ones.模式级别的挂钩对于工作流固有的行为有意义 - 例如,始终在存档上生成决策记录的 ADR 模式。但是特定于项目的挂钩(通知特定的 Slack 通道、更新特定于项目的错误日志、运行仅存在于您的存储库中的脚本)属于项目配置 ( openspec/config.yaml )。相同的机制可以应用于两个级别 - 模式定义工作流程的默认挂钩,配置添加或覆盖特定于项目的挂钩。
The only core change required is reading hooks from both schema.yaml and config.yaml, and surfacing them at the right moment. Everything else is defined by each schema or project, keeping the core minimal.唯一需要的核心更改是从schema.yaml和config.yaml读取hooks ,并在正确的时刻显示它们。其他一切都由每个模式或项目定义,保持核心最小化。
I fully agree with your point, and I believe we should provide such capabilities for all commands. I think the artifacts defined in our schema.yaml should support hooks.
Will use this. While syncing the canonical spec.md we want to populate a Change History table. Only way currently is to add directives in a CLAUDE.md at openspec root. Havent found other ways that worked correctly, but this pre/post hooks would allow such things.
Activity
21st-connector commentedon Feb 9, 2026
Task completed.
The enrichment comment has been posted on issue #682. Here's a summary of what was included:
Existing infrastructure: OpenSpec uses Commander.js
preAction/postActionhooks internally for telemetry and validation, but has no user-extensible hook/plugin system.Archive code analysis: Identified the exact integration points in
src/core/archive.tswherepre-archiveHookandpost-archiveHookwould fit within the 4-step archive workflow (validation → task check → spec updates → directory move).Configuration path: The
ProjectConfigSchemainsrc/core/project-config.tswould need to be extended with hook fields, with Zod validation.Related issues: Linked 6 related issues — #328 (Claude Hooks), #331 (auto-update project.md on archive), #369 (post-archive actions), #641 (error correction log), plus archive bugs #412 and #498.
Implementation considerations: Highlighted 4 key challenges — no existing subprocess spawning infrastructure, lack of atomicity/rollback in the current archive flow, error propagation contract design, and cross-platform script execution needs.
View full conversation
Powered by 1Code
21st-connector commentedon Feb 9, 2026
Issue Analysis
Thanks for the detailed feature request. Here's an analysis based on the current codebase to help developers get started.
Existing Hook Infrastructure
OpenSpec already uses Commander.js
.hook()for CLI lifecycle management, but these are internal framework hooks — not user-extensible:preAction/postActionhooks insrc/cli/index.ts:71-88— used for telemetry andNO_COLORsetuppreActionhooks — used for validation (src/commands/config.ts:31-37), deprecation warnings (src/cli/index.ts:210-212,src/commands/spec.ts:117-119), and experimental notices (src/commands/schema.ts:296-298)There is no existing user-extensible hook/plugin system, no EventEmitter pattern, and no middleware chain. Extensibility today is configuration-driven (
openspec/config.yaml) rather than code-driven.Archive Command — Key Integration Points
The archive implementation lives in
src/core/archive.ts(340 lines), specifically theArchiveCommand.execute()method at line 51. The current workflow is:openspec/changes/archive/<date>-<name>/A
pre-archiveHookwould logically fit between validation (step 1) and the spec update/move operations. Apost-archiveHookwould fit after step 4 (line 287), once the directory has been moved.Configuration Approach
The project config is loaded in
src/core/project-config.ts:66-161fromopenspec/config.yaml(or.yml), validated via Zod (ProjectConfigSchemaat line 19). The current schema supports:Adding hook configuration would require extending
ProjectConfigSchemawith new fields (e.g.,hooks.pre-archive,hooks.post-archivepointing to script paths) and adding validation logic.Related Issues
Several existing issues describe use cases that a hook system would address:
project.mdduring archive — a naturalpost-archiveHookuse caseArchive-related bugs that hook authors would need to be aware of:
openspec validate --strictsucceeds, butopenspec archivefails validationImplementation Considerations
Script execution model: The codebase is pure TypeScript/ESM with no existing subprocess spawning for user scripts. A hook runner would need to be built from scratch — likely using Node.js
child_process.execFileorspawnto run user-defined scripts.Atomicity concern (raised in the issue): The current archive operation is not transactional — spec updates (step 3) and the directory move (step 4) happen sequentially. If a
post-archiveHookfails, rollback logic would need to be added, which doesn't exist today.Error propagation: The
execute()method currently returnsvoidand communicates viaconsole.log/ thrown errors. Hooks would need a defined contract for success/failure signaling (exit codes, stdout parsing, etc.).Cross-platform: The archive already handles Windows-specific issues (
EPERM/EXDEVfallbacks at lines 36-48). Any hook execution path would also need Windows compatibility (e.g.,.cmd/.ps1scripts alongside shell scripts).🤖 Generated with Claude Code
lsmonki commentedon Feb 9, 2026
This is closely related to the discussion in #557 — specifically this comment where I proposed lifecycle hooks as LLM instructions defined in
schema.yaml, rather than shell scripts.The core idea: instead of spawning subprocesses, hooks are prompts that the LLM follows at lifecycle points (post-archive, post-sync, etc.):
This covers the error log consolidation scenario described here — without needing subprocess infrastructure, rollback mechanics, or cross-platform script handling.
For cases where deterministic execution is needed (notifications, CI triggers, external API calls), hooks could also support a
runfield:This way
instructionhandles the LLM-driven work (reasoning over artifacts, updating docs) andrunhandles the programmatic side (sending a webhook, updating a database). Both are optional — use one, the other, or both.Hooks at the schema level make sense for behavior that's inherent to the workflow — e.g., an ADR schema that always generates decision records on archive. But project-specific hooks (notify a particular Slack channel, update a project-specific error log, run a script that only exists in your repo) belong in the project config (
openspec/config.yaml). The same mechanism could apply at both levels — schema defines default hooks for the workflow, config adds or overrides with project-specific ones.The only core change required is reading
hooksfrom bothschema.yamlandconfig.yaml, and surfacing them at the right moment. Everything else is defined by each schema or project, keeping the core minimal.dragonTalon commentedon Feb 9, 2026
I fully agree with your point, and I believe we should provide such capabilities for all commands. I think the artifacts defined in our schema.yaml should support hooks.
feat: add extensible lifecycle hooks for schemas and project config (F…
Feat : Add Extensible Hook (Fission-AI#682)
mathieufortin01 commentedon Jun 8, 2026
Will use this. While syncing the canonical spec.md we want to populate a Change History table. Only way currently is to add directives in a CLAUDE.md at openspec root. Havent found other ways that worked correctly, but this pre/post hooks would allow such things.