From c85b8d3f3d61561d9639ef8413d77e988fa0b55b Mon Sep 17 00:00:00 2001 From: Peter Krenesky Date: Sat, 27 Jun 2026 19:08:42 -0700 Subject: [PATCH] Add Track node archetype between Plan and Task Promote Track from a 'track:' field to a first-class node archetype so the containment chain is Spec -> Plan -> Track -> Task, enabling a four-level tracker mapping (e.g. Jira Initiative -> Epic -> Story -> Sub-task). Adds the Track schema + skeleton, registers it in the manifest (Plan now contains [Track, Task]), keeps the legacy 'track:' field valid for backward compatibility, and bumps the module to 0.2.0. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 7 +-- package.json | 2 +- pyproject.toml | 2 +- spec_artifacts_process/manifest.yaml | 25 +++++++++- .../schemas/task-frontmatter.schema.json | 4 ++ .../schemas/track-frontmatter.schema.json | 49 +++++++++++++++++++ spec_artifacts_process/skeletons/Track.md | 35 +++++++++++++ tests/test_manifest.py | 46 +++++++++++++++++ 8 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 spec_artifacts_process/schemas/track-frontmatter.schema.json create mode 100644 spec_artifacts_process/skeletons/Track.md diff --git a/README.md b/README.md index 37ac8b9..f19ba2a 100644 --- a/README.md +++ b/README.md @@ -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). @@ -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`. | diff --git a/package.json b/package.json index 940389d..ce7b2fe 100644 --- a/package.json +++ b/package.json @@ -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 ", "keywords": [ diff --git a/pyproject.toml b/pyproject.toml index 8516252..5ece384 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 "] packages = [{ include = "spec_artifacts_process" }] include = [ diff --git a/spec_artifacts_process/manifest.yaml b/spec_artifacts_process/manifest.yaml index f7fdfda..ba4680c 100644 --- a/spec_artifacts_process/manifest.yaml +++ b/spec_artifacts_process/manifest.yaml @@ -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 @@ -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 @@ -53,6 +61,7 @@ grammars: doc_kinds: - adr - plan + - track - review - spec-review - test-matrix @@ -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 @@ -183,6 +203,7 @@ artifact_types: doc_kinds: - adr - plan +- track - review - spec-review - test-matrix diff --git a/spec_artifacts_process/schemas/task-frontmatter.schema.json b/spec_artifacts_process/schemas/task-frontmatter.schema.json index c1034e9..08402ab 100644 --- a/spec_artifacts_process/schemas/task-frontmatter.schema.json +++ b/spec_artifacts_process/schemas/task-frontmatter.schema.json @@ -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" }, diff --git a/spec_artifacts_process/schemas/track-frontmatter.schema.json b/spec_artifacts_process/schemas/track-frontmatter.schema.json new file mode 100644 index 0000000..6e07b3a --- /dev/null +++ b/spec_artifacts_process/schemas/track-frontmatter.schema.json @@ -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" + } + } + } + } + } +} diff --git a/spec_artifacts_process/skeletons/Track.md b/spec_artifacts_process/skeletons/Track.md new file mode 100644 index 0000000..50543d2 --- /dev/null +++ b/spec_artifacts_process/skeletons/Track.md @@ -0,0 +1,35 @@ +--- +id: TRK-001 +title: "" +type: Track +relationships: + - target: "ix://agent-ix//Task-001" + type: contains +--- + + +## Summary + + + +## Tasks + + diff --git a/tests/test_manifest.py b/tests/test_manifest.py index eb29034..c17e7f1 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -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: