Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ All notable changes to this project will be documented in this file.

## [0.32.0] - 2026-02-16

### Added (0.32.0)
### Added

- **Enhanced module manifest security and integrity** (OpenSpec change `arch-06-enhanced-manifest-security`, fixes [#208](https://github.com/nold-ai/specfact-cli/issues/208))
- **Enhanced module manifest security and integrity** (arch-06, fixes [#208](https://github.com/nold-ai/specfact-cli/issues/208))
- Publisher and integrity metadata in `module-package.yaml` (`publisher`, `integrity.checksum`, optional `integrity.signature`).
- Versioned dependency entries (`module_dependencies_versioned`, `pip_dependencies_versioned`) with name and version specifier.
- `crypto_validator`: checksum verification (sha256/sha384/sha512) and optional signature verification.
Expand All @@ -22,6 +22,13 @@ All notable changes to this project will be documented in this file.
- Signing automation: `scripts/sign-module.sh` and `.github/workflows/sign-modules.yml` for checksum generation.
- Documentation: `docs/reference/module-security.md` and architecture updates for module trust and integrity lifecycle.

- **Schema extension system** (arch-07, Resolves [#213](https://github.com/nold-ai/specfact-cli/issues/213))
- `extensions` dict field on `Feature` and `ProjectBundle` with namespace-prefixed keys (e.g. `backlog.ado_work_item_id`).
- Type-safe `get_extension(module_name, field, default=None)` and `set_extension(module_name, field, value)` with contract enforcement.
- Optional `schema_extensions` in `module-package.yaml` to declare target model, field, type_hint, and description.
- `ExtensionRegistry` for collision detection and introspection; module registration loads and validates schema extensions.
- Guide: [Extending ProjectBundle](https://docs.specfact.io/guides/extending-projectbundle/).

---

## [0.31.1] - 2026-02-16
Expand Down
2 changes: 2 additions & 0 deletions docs/_layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ <h2 class="docs-sidebar-title">
<li><a href="{{ '/guides/command-chains/' | relative_url }}">Command Chains</a></li>
<li><a href="{{ '/guides/agile-scrum-workflows/' | relative_url }}">Agile/Scrum Workflows</a></li>
<li><a href="{{ '/guides/creating-custom-bridges/' | relative_url }}">Creating Custom Bridges</a></li>
<li><a href="{{ '/guides/extending-projectbundle/' | relative_url }}">Extending ProjectBundle</a></li>
<li><a href="{{ '/guides/using-module-security-and-extensions/' | relative_url }}">Using Module Security and Extensions</a></li>
<li><a href="{{ '/brownfield-engineer/' | relative_url }}">Working With Existing Code</a></li>
<li><a href="{{ '/brownfield-journey/' | relative_url }}">Existing Code Journey</a></li>
<li><a href="{{ '/guides/sidecar-validation/' | relative_url }}">Sidecar Validation</a></li>
Expand Down
82 changes: 82 additions & 0 deletions docs/guides/extending-projectbundle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
layout: default
title: Extending ProjectBundle
permalink: /guides/extending-projectbundle/
description: Add namespaced custom fields to Feature and ProjectBundle without modifying core models.
---

# Extending ProjectBundle

Modules can extend `Feature` and `ProjectBundle` with custom metadata using the **schema extension system** (arch-07). Extensions use namespace-prefixed keys so multiple modules can store data without conflicts.

## Overview

- **`extensions`** – A dict on `Feature` and `ProjectBundle` that stores module-specific data under keys like `backlog.ado_work_item_id` or `sync.last_sync_timestamp`.
- **`get_extension(module_name, field, default=None)`** – Read a value.
- **`set_extension(module_name, field, value)`** – Write a value.
- **`schema_extensions`** – Optional declaration in `module-package.yaml` so the CLI can validate and introspect which fields a module uses.

## Using Extensions in Code

```python
from specfact_cli.models.plan import Feature
from specfact_cli.models.project import ProjectBundle

# On a Feature (e.g. from a bundle)
feature.set_extension("backlog", "ado_work_item_id", "123456")
value = feature.get_extension("backlog", "ado_work_item_id") # "123456"
missing = feature.get_extension("backlog", "missing", default="default") # "default"

# On a ProjectBundle
bundle.set_extension("sync", "last_sync_timestamp", "2025-01-15T12:00:00Z")
ts = bundle.get_extension("sync", "last_sync_timestamp")
```

**Rules:**

- `module_name`: lowercase, alphanumeric plus underscores/hyphens, **no dots** (e.g. `backlog`, `sync`).
- `field`: lowercase, alphanumeric plus underscores (e.g. `ado_work_item_id`).
- Keys are stored as `module_name.field` (e.g. `backlog.ado_work_item_id`).

## Declaring Extensions in the Manifest

In `module-package.yaml` you can declare which extensions your module uses so the CLI can detect collisions and support introspection:

```yaml
name: backlog
version: "0.1.0"
commands: [backlog]

schema_extensions:
- target: Feature
field: ado_work_item_id
type_hint: str
description: Azure DevOps work item ID for sync
- target: Feature
field: jira_issue_key
type_hint: str
description: Jira issue key when using Jira adapter
- target: ProjectBundle
field: last_sync_timestamp
type_hint: str
description: ISO timestamp of last sync
```

- **target** – `Feature` or `ProjectBundle`.
- **field** – Snake_case field name (must match `[a-z][a-z0-9_]*`).
- **type_hint** – Documentation only (e.g. `str`, `int`).
- **description** – Human-readable description.

If two modules declare the same `(target, field)` (e.g. both declare `Feature.ado_work_item_id`), the second module’s schema extensions are skipped and an error is logged.

## Best Practices

- Use a single logical namespace per module (the module name).
- Prefer short, clear field names (`ado_work_item_id`, `last_sync_timestamp`).
- Document extensions in `schema_extensions` so other tools and docs can introspect them.
- Do not rely on extension values for core behavior; keep them as optional metadata.

## Backward Compatibility

- Existing bundles and features without an `extensions` field load with `extensions = {}`.
- Modules that omit `schema_extensions` load and run normally; no extensions are registered for them.
147 changes: 147 additions & 0 deletions docs/guides/using-module-security-and-extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
layout: default
title: Using Module Security and Schema Extensions
permalink: /guides/using-module-security-and-extensions/
description: How to use arch-06 (module security) and arch-07 (schema extensions) from CLI commands and as a module author.
nav_order: 22
---

# Using Module Security and Schema Extensions

With **arch-06** (manifest security) and **arch-07** (schema extension system) in place, you can use verified modules and store module-specific metadata on bundles and features. This guide shows how to utilize these features from the CLI and in your own modules.

## Quick reference

| Capability | What it does | Where to read more |
|------------|--------------|--------------------|
| **arch-06** | Publisher + integrity (checksum/signature) on module manifests; versioned dependencies | [Module Security](/reference/module-security/) |
| **arch-07** | `extensions` dict on Feature/ProjectBundle; `get_extension`/`set_extension`; `schema_extensions` in manifest | [Extending ProjectBundle](/guides/extending-projectbundle/) |

---

## Using arch-06 (module security)

### As a CLI user (consuming modules)

- **Verified modules**: When you run any command that loads modules (e.g. `specfact backlog ...`, `specfact project ...`), the registry discovers modules and, when a module has `integrity.checksum` in its `module-package.yaml`, verifies the manifest checksum before registering. If verification fails, that module is skipped and a warning is logged; other modules still load.
- **Unsigned modules**: Modules without `integrity` metadata are allowed by default (backward compatible). To document explicit opt-in in strict environments, set:
```bash
export SPECFACT_ALLOW_UNSIGNED=1
```
- **Versioned dependencies**: Manifests can declare `module_dependencies_versioned` and `pip_dependencies_versioned` (each entry: `name`, `version_specifier`) for install-time resolution. You don’t need to do anything special; the installer uses these when present.

You don’t run a separate β€œverify” command; verification happens automatically at module registration when the CLI starts.

### As a module author (publishing a module)

1. **Add publisher and integrity to `module-package.yaml`** (optional but recommended):

```yaml
name: my-module
version: "0.1.0"
commands: [my-group]

publisher:
name: "Your Name or Org"
email: "contact@example.com"

integrity:
checksum: "sha256:<hex>" # Required for verification
signature: "<base64>" # Optional; requires trusted key on consumer side
```

2. **Generate the checksum** using the bundled script:

```bash
./scripts/sign-module.sh path/to/module-package.yaml
# Output: sha256:<hex>
# Add that value to integrity.checksum in the manifest
```

3. **CI**: Use `.github/workflows/sign-modules.yml` (or equivalent) to produce or validate checksums when manifest files change.

4. **Versioned dependencies** (optional):

```yaml
module_dependencies_versioned:
- name: backlog-core
version_specifier: ">=0.2.0"
pip_dependencies_versioned:
- name: requests
version_specifier: ">=2.28.0"
```

Details: [Module Security](/reference/module-security/).

---

## Using arch-07 (schema extensions)

### As a CLI user (running commands that use extensions)

Several commands already read or write extension data on `ProjectBundle` (and its manifest). You use them as usual; extensions are persisted with the bundle.

- **Link a backlog provider** (writes `backlog_core.backlog_config` on project metadata):
```bash
specfact project link-backlog --bundle my-bundle --adapter github --project-id my-org/my-repo
```
- **Health check and other project commands** read that same extension to resolve adapter/project/template:
```bash
specfact project health-check --bundle my-bundle
```

Any command that loads a bundle (e.g. `specfact plan ...`, `specfact sync ...`, `specfact spec ...`) loads the full bundle including `extensions`; round-trip save keeps extension data. So you don’t need a special β€œextensions” command to benefit from themβ€”they’re part of the bundle.

**Introspecting registered extensions (programmatic):** There is no `specfact extensions list` CLI yet. From Python you can call:

```python
from specfact_cli.registry.extension_registry import get_extension_registry
all_exts = get_extension_registry().list_all() # dict: module_name -> list of SchemaExtension
```

### As a module author (using extensions in your commands)

1. **Declare extensions** in `module-package.yaml` so the CLI can validate and avoid collisions:

```yaml
schema_extensions:
- target: Feature
field: my_custom_id
type_hint: str
description: My module’s external ID for this feature
- target: ProjectBundle
field: last_sync_ts
type_hint: str
description: ISO timestamp of last sync
```

2. **In your command code**, when you have a `ProjectBundle` or `Feature` (e.g. from `load_bundle_with_progress` or from a plan bundle):

```python
from specfact_cli.models.plan import Feature
from specfact_cli.models.project import ProjectBundle

# On a Feature
feature.set_extension("my_module", "my_custom_id", "EXT-123")
value = feature.get_extension("my_module", "my_custom_id") # "EXT-123"
missing = feature.get_extension("my_module", "other", default="n/a") # "n/a"

# On ProjectBundle (e.g. bundle.manifest.project_metadata or bundle itself)
bundle.set_extension("my_module", "last_sync_ts", "2026-02-16T12:00:00Z")
ts = bundle.get_extension("my_module", "last_sync_ts")
```

3. **Naming rules**: `module_name`: lowercase, alphanumeric + underscores/hyphens, **no dots**. `field`: lowercase, alphanumeric + underscores. Keys are stored as `module_name.field` (e.g. `my_module.my_custom_id`).

4. **Project metadata**: The built-in `project link-backlog` command uses **project_metadata** (on the bundle manifest), which also supports `get_extension`/`set_extension` with the same `module_name.field` convention (e.g. `backlog_core.backlog_config`). Use the same pattern for your module’s config stored on the project.

Full API and examples: [Extending ProjectBundle](/guides/extending-projectbundle/).

---

## Summary

- **arch-06**: Use `scripts/sign-module.sh` and `integrity`/`publisher` in manifests; consumers get automatic checksum verification at registration; set `SPECFACT_ALLOW_UNSIGNED=1` if you explicitly allow unsigned modules.
- **arch-07**: Use `get_extension`/`set_extension` on Feature and ProjectBundle in your module code; declare `schema_extensions` in `module-package.yaml`; use existing commands like `specfact project link-backlog` and `specfact project health-check` to see extensions in action.

For deeper reference: [Module Security](/reference/module-security/), [Extending ProjectBundle](/guides/extending-projectbundle/), [Architecture](/reference/architecture/).
8 changes: 8 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ Why this matters:
- Interfaces and contracts keep feature development isolated and safer to iterate.
- Pending OpenSpec-driven module changes can land incrementally with lower migration risk.

**Module security and extensions:**

- **[Using Module Security and Extensions](guides/using-module-security-and-extensions.md)** - How to use verified modules (arch-06) and schema extensions (arch-07) from the CLI and as a module author
- **[Extending ProjectBundle](guides/extending-projectbundle.md)** - Declare and use namespaced extension fields on Feature/ProjectBundle
- **[Module Security](reference/module-security.md)** - Publisher, integrity (checksum/signature), and versioned dependencies

## πŸ“š Documentation

### Guides
Expand All @@ -89,6 +95,8 @@ Why this matters:
- **[Backlog Dependency Analysis](guides/backlog-dependency-analysis.md)** - Analyze critical path, cycles, orphans, and dependency impact from backlog graph data
- **[Backlog Delta Commands](guides/backlog-delta-commands.md)** - Track backlog graph changes under `specfact backlog delta`
- **[Project DevOps Flow](guides/project-devops-flow.md)** - Run plan/develop/review/release/monitor stage actions from one command surface
- **[Extending ProjectBundle](guides/extending-projectbundle.md)** - Add namespaced custom fields to Feature/ProjectBundle (arch-07)
- **[Using Module Security and Extensions](guides/using-module-security-and-extensions.md)** - Use arch-06 (module security) and arch-07 (schema extensions) from CLI and as a module author
- **[Sidecar Validation](guides/sidecar-validation.md)** πŸ†• - Validate external codebases without modifying source
- **[UX Features](guides/ux-features.md)** - Progressive disclosure, context detection, intelligent suggestions
- **[Use Cases](guides/use-cases.md)** - Real-world scenarios and workflows
Expand Down
10 changes: 10 additions & 0 deletions docs/reference/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ SpecFact CLI implements a **contract-driven development** framework through thre
- Invalid bridge declarations are non-fatal and skipped with warnings.
- Protocol compliance reporting uses effective runtime interface detection and logs one aggregate summary line.

## Schema Extension System

`arch-07-schema-extension-system` lets modules extend `Feature` and `ProjectBundle` with namespaced custom fields without changing core models.

- **Extensions field**: `Feature` and `ProjectBundle` have an `extensions: dict[str, Any]` field (default empty dict). Keys use the form `module_name.field` (e.g. `backlog.ado_work_item_id`).
- **Accessors**: `get_extension(module_name, field, default=None)` and `set_extension(module_name, field, value)` enforce namespace format and type safety via contracts.
- **Manifest**: Optional `schema_extensions` in `module-package.yaml` declare target model, field name, type hint, and description. Lifecycle loads these and registers them in a global extension registry.
- **Collision detection**: If two modules declare the same (target, field), the second registration is rejected and an error is logged; module command registration continues.
- See [Extending ProjectBundle](/guides/extending-projectbundle/) for usage and best practices.

## Module System Foundation

SpecFact is transitioning from hard-wired command wiring to a module-first architecture.
Expand Down
6 changes: 3 additions & 3 deletions modules/backlog-core/src/backlog_core/graph/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from __future__ import annotations

from datetime import UTC, datetime
from enum import Enum
from enum import StrEnum
from typing import Any

from pydantic import BaseModel, Field


class ItemType(str, Enum):
class ItemType(StrEnum):
"""Normalized backlog item types."""

EPIC = "epic"
Expand All @@ -21,7 +21,7 @@ class ItemType(str, Enum):
CUSTOM = "custom"


class DependencyType(str, Enum):
class DependencyType(StrEnum):
"""Normalized dependency relationship types."""

PARENT_CHILD = "parent_child"
Expand Down
12 changes: 7 additions & 5 deletions openspec/CHANGE_ORDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Changes are grouped by **module** and prefixed with **`<module>-NN-`** so implem
| backlog-scrum-01-standup-exceptions-first | 2026-02-11 |
| backlog-core-03-refine-writeback-field-splitting | 2026-02-12 |
| sidecar-01-flask-support | 2026-02-12 |
| ci-01-pr-orchestrator-log-artifacts | 2026-02-16 |
| arch-06-enhanced-manifest-security | 2026-02-16 |
| arch-07-schema-extension-system | 2026-02-16 |

### Pending

Expand All @@ -46,8 +49,7 @@ These are derived extensions of the same 2026-02-15 plan and are required to ope

| Module | Order | Change folder | GitHub # | Blocked by |
|--------|-------|---------------|----------|------------|
| arch | 06 | arch-06-enhanced-manifest-security | [#208](https://github.com/nold-ai/specfact-cli/issues/208) | arch-05 βœ… |
| arch | 07 | arch-07-schema-extension-system | [#213](https://github.com/nold-ai/specfact-cli/issues/213) | arch-04 βœ… |
| β€” | β€” | arch-06, arch-07 implemented 2026-02-16 (see Implemented above) | β€” | β€” |

### Marketplace (module distribution)

Expand All @@ -69,7 +71,7 @@ These are derived extensions of the same 2026-02-15 plan and are required to ope

| Module | Order | Change folder | GitHub # | Blocked by |
|--------|-------|----------------|----------|------------|
| ci | 01 | ci-01-pr-orchestrator-log-artifacts | [#260](https://github.com/nold-ai/specfact-cli/issues/260) | β€” |
| β€” | β€” | ci-01 implemented 2026-02-16 (see Implemented above) | β€” | β€” |

### backlog-core (required by all backlog-* modules)

Expand Down Expand Up @@ -244,8 +246,8 @@ Dependencies flow left-to-right; a wave may start once all its hard blockers are

- **Wave 0** βœ… **Complete** β€” arch-01 through arch-05 (modular CLI foundation, bridge registry)

- **Wave 1 β€” Platform extensions + cross-cutting foundations** (all unblocked now):
- arch-06, arch-07
- **Wave 1 β€” Platform extensions + cross-cutting foundations** (arch-06 βœ…, arch-07 βœ…, ci-01 βœ…):
- arch-06 βœ…, arch-07 βœ…, ci-01 βœ…
- policy-engine-01, patch-mode-01
- backlog-core-01
- validation-01, sidecar-01 βœ…, bundle-mapper-01
Expand Down
Loading
Loading