Skip to content
Open
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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# spec-artifacts-process

> Filament Module: process artifact templates (ADR, Plan, Task, Review, Finding, TestMatrix, Standard) — Jinja .md.j2 templates
> Filament Module: process artifact templates (ADR, Plan, Track, Task, Review, Finding, TestMatrix, Standard) — Jinja .md.j2 templates

Agent-IX Filament module loaded by [`quire-cli`](https://github.com/agent-ix/quire-cli) and [`quoin`](https://github.com/agent-ix/quoin).

Expand Down Expand Up @@ -37,8 +37,9 @@ quire validate spec/**/*.md --module node_modules/@agent-ix/spec-artifacts-proce
| Kind | ID pattern | Description |
|------|------------|-------------|
| ADR | `ADR-{next:03d}` | Architecture Decision Record capturing a decision, its context and consequences; moves through `proposed → accepted → superseded → rejected` states and links via `supersedes`/`superseded_by`/`relates_to`/`depends_on`. |
| Plan | `Plan-{next:03d}` | An implementation plan that owns a set of member Tasks (expected artifact `Task`); links via `contains`/`depends_on`/`references`. |
| Task | `Task-{next:03d}` | A single unit of implementation work; links via `depends_on`/`verifies`/`references`. |
| Plan | `Plan-{next:03d}` | An implementation plan that owns member Tracks and/or Tasks (expected artifacts `Track`, `Task`); links via `contains`/`depends_on`/`references`. |
| Track | `TRK-{next:03d}` | A first-class node between Plan and Task that groups related Tasks into a parallelizable workstream (expected artifact `Task`); enables a Spec → Plan → Track → Task hierarchy. Links via `contains`/`depends_on`/`references`. |
| Task | `Task-{next:03d}` | A single unit of implementation work; links via `depends_on`/`verifies`/`references`. Retains an optional legacy `track:` string field (superseded by the nodal `Track`). |
| Review | `Review-{next:03d}` | A review document that owns a set of member Findings (expected artifact `Finding`); top-level nav item, links via `reviews`/`references`. |
| Finding | `Finding-{next:03d}` | An individual review finding/issue; links via `found_in`/`blocks`/`references`. |
| TestMatrix | `TestMatrix-{next:03d}` | A requirement-to-test coverage matrix; links via `covers`/`references`. |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@agent-ix/spec-artifacts-process",
"version": "0.0.0-managed",
"description": "Filament Module: process-artifact templates (ADR, Plan, Task, Review, Finding, TestMatrix, Standard)",
"description": "Filament Module: process-artifact templates (ADR, Plan, Track, Task, Review, Finding, TestMatrix, Standard)",
"license": "AGPL-3.0-or-later",
"author": "Agent IX <agents@agent-ix.dev>",
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = "AGPL-3.0-or-later"
[tool.poetry]
name = "spec_artifacts_process"
version = "0.1.0" # Placeholder, dynamically replaced
description = "Filament Module: process-artifact templates (ADR, Plan, Task, Review, Finding, TestMatrix, Standard) — Jinja .md.j2 templates and archetypes"
description = "Filament Module: process-artifact templates (ADR, Plan, Track, Task, Review, Finding, TestMatrix, Standard) — Jinja .md.j2 templates and archetypes"
authors = ["Agent IX <agents@agent-ix.dev>"]
packages = [{ include = "spec_artifacts_process" }]
include = [
Expand Down
25 changes: 23 additions & 2 deletions spec_artifacts_process/manifest.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
manifest_version: 1.0.0
name: spec-artifacts-process
version: 0.1.0
description: "Filament Module: process artifact templates (ADR, Plan, Task, Review, Finding, TestMatrix, Standard) \u2014 Jinja .md.j2 templates"
version: 0.2.0
description: "Filament Module: process artifact templates (ADR, Plan, Track, Task, Review, Finding, TestMatrix, Standard) \u2014 Jinja .md.j2 templates"
archetypes:
- kind: adr
name: ADR
Expand All @@ -17,6 +17,14 @@ archetypes:
name: Plan
doc_backed: true
supports_membership: true
composition:
expected_artifacts:
- Track
- Task
- kind: track
name: Track
doc_backed: true
supports_membership: true
composition:
expected_artifacts:
- Task
Expand Down Expand Up @@ -53,6 +61,7 @@ grammars:
doc_kinds:
- adr
- plan
- track
- review
- spec-review
- test-matrix
Expand Down Expand Up @@ -83,6 +92,17 @@ artifact_types:
- references
examples: []
lint_rules_ref: []
- name: Track
grammar_ref: process-artifacts
frontmatter_schema_ref: schemas/track-frontmatter.schema.json
defaults:
id_pattern: TRK-{next:03d}
allowed_links:
- contains
- depends_on
- references
examples: []
lint_rules_ref: []
- name: Task
grammar_ref: process-artifacts
frontmatter_schema_ref: schemas/task-frontmatter.schema.json
Expand Down Expand Up @@ -183,6 +203,7 @@ artifact_types:
doc_kinds:
- adr
- plan
- track
- review
- spec-review
- test-matrix
Expand Down
4 changes: 4 additions & 0 deletions spec_artifacts_process/schemas/task-frontmatter.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
"type": {
"const": "Task"
},
"track": {
"type": "string",
"description": "Legacy grouping label (e.g. \"C\"). Retained for backward compatibility; the nodal form is a first-class Track artifact that `contains` this Task."
},
"object": {
"type": "string"
},
Expand Down
49 changes: 49 additions & 0 deletions spec_artifacts_process/schemas/track-frontmatter.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"id",
"title",
"type"
],
"additionalProperties": true,
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z]{2,4}-[0-9]+$"
},
"title": {
"type": "string",
"minLength": 1
},
"type": {
"const": "Track"
},
"object": {
"type": "string"
},
"relationships": {
"type": "array",
"items": {
"type": "object",
"required": [
"target",
"type"
],
"additionalProperties": false,
"properties": {
"target": {
"type": "string",
"pattern": "^ix://"
},
"type": {
"type": "string"
},
"cardinality": {
"type": "string"
}
}
}
}
}
}
35 changes: 35 additions & 0 deletions spec_artifacts_process/skeletons/Track.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
id: TRK-001
title: "<track name>"
type: Track
relationships:
- target: "ix://agent-ix/<repo>/Task-001"
type: contains
---
<!-- Track authoring skeleton (spec-artifacts-process). A Track is a first-class
node that sits BETWEEN a Plan and its Tasks, grouping related Tasks into a
parallelizable workstream. It lets a four-level hierarchy
(Spec -> Plan -> Track -> Task) map onto external trackers (e.g. Jira's
Initiative -> Epic -> Story -> Sub-task).

Contract (validated by `quire validate`):
- Frontmatter: `type: Track`; `id` matches ^[A-Za-z]{2,4}-[0-9]+$
(e.g. TRK-001).
- A Track `contains` its member Tasks (mirroring how a Plan `contains`
its members); the parent Plan declares the Plan -> Track edge from its
own side. Each `relationships[].target` is an `ix://` URI.

Backward compatibility: the legacy `track:` string field on a Task
(e.g. `track: C`) remains valid. A Track node is the nodal form of that
grouping; migrate by creating one Track per distinct legacy `track:` value
and linking the corresponding Tasks via `contains`. -->

## Summary

<!-- One or two sentences: what workstream this Track groups and why its Tasks
belong together. -->

## Tasks

<!-- List the member Tasks of this Track (each also declared via a `contains`
relationship in frontmatter). -->
46 changes: 46 additions & 0 deletions tests/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,52 @@ def test_spec_review_archetype_registered_with_findings_validation() -> None:
]


def test_track_archetype_registered_as_node_between_plan_and_task() -> None:
"""Track is a first-class node inserted BETWEEN Plan and Task so a four-level
Spec -> Plan -> Track -> Task hierarchy can map onto external trackers
(TC-010). Plan expects Track (and, for backward compatibility, still Task);
Track expects Task. The legacy `track:` field on Task remains valid."""
manifest = yaml.safe_load(MANIFEST_PATH.read_text())

# Registered as an archetype with Task as its expected member.
archetypes = {a["name"]: a for a in manifest["archetypes"]}
assert "Track" in archetypes, "Track archetype must be registered"
track = archetypes["Track"]
assert track["supports_membership"] is True
assert track["composition"]["expected_artifacts"] == ["Task"]

# Inserted into the containment chain: Plan now expects Track (Task kept
# for backward compatibility with legacy Plan -> Task bundles).
plan = archetypes["Plan"]
assert plan["composition"]["expected_artifacts"] == ["Track", "Task"]

# Registered as an artifact type with schema + container links.
track_type = next(t for t in manifest["artifact_types"] if t["name"] == "Track")
assert (
track_type["frontmatter_schema_ref"]
== "schemas/track-frontmatter.schema.json"
)
assert "contains" in track_type["allowed_links"]

# Schema validates the Track node shape.
schema_path = pack.PACK_ROOT / "schemas" / "track-frontmatter.schema.json"
assert schema_path.is_file()
schema = json.loads(schema_path.read_text())
assert schema["properties"]["type"]["const"] == "Track"

# Authoring skeleton exists so `quoin write --types Track` resolves it.
skeleton = pack.PACK_ROOT / "skeletons" / "Track.md"
assert skeleton.is_file()
assert "type: Track" in skeleton.read_text()

# Backward compatibility: legacy `track:` field still valid on Task.
task_schema = json.loads(
(pack.PACK_ROOT / "schemas" / "task-frontmatter.schema.json").read_text()
)
assert task_schema["properties"]["track"]["type"] == "string"
assert "track" not in task_schema.get("required", [])


def test_manifest_validates_against_fr035_schema() -> None:
"""Skip if jsonschema lacks draft 2020-12 (use CI check-jsonschema instead)."""
try:
Expand Down
Loading