diff --git a/openspec/changes/upstream-contribution/.openspec.yaml b/openspec/changes/upstream-contribution/.openspec.yaml new file mode 100644 index 000000000..e14f3223d --- /dev/null +++ b/openspec/changes/upstream-contribution/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-13 diff --git a/openspec/changes/upstream-contribution/design.md b/openspec/changes/upstream-contribution/design.md new file mode 100644 index 000000000..663930e2b --- /dev/null +++ b/openspec/changes/upstream-contribution/design.md @@ -0,0 +1,155 @@ +# Design: Upstream Contribution Strategy + +## Architecture Decisions + +### AD-1: contextFiles type adaptation (v1.3.0) + +Upstream changed `ApplyInstructions.contextFiles` from `Record` to `Record` to support glob artifact outputs. + +**Impact on fork code:** +- `src/core/pipeline/runner.ts` — reads contextFiles for gate synthesis context +- `src/core/orchestration/resolver.ts` — reads contextFiles for task grouping +- `src/commands/workflow/instructions.ts` — covers injection reads contextFiles + +**Resolution:** Handled during rebase (2026-04-13). All fork code now treats contextFiles values as `string[]`. The rebase was clean — auto-merge succeeded for `shared.ts` type change. + +### AD-2: update.ts transformer composition + +Fork adds `composeTransformers(overlayTransformer, toolTransformer)` pattern. Upstream added `pi` tool to the hyphen-command condition. + +**Resolution:** Merged both — `pi` added to condition, overlay composition preserved: +```typescript +const toolTransformer = (tool.value === 'opencode' || tool.value === 'pi') + ? transformToHyphenCommands : undefined; +const transformer = composeTransformers(overlayTransformer, toolTransformer); +``` + +### AD-5: Workspace foundation impact (added 2026-05-04) + +Upstream merged `workspace-foundation` (PR #1045, archived as canonical spec) on 2026-05-04. This introduces a cross-repo planning model: workspaces live at `/workspaces//`, link multiple repos via stable names, and own `changes/` at the workspace root. + +Six slices are planned (1+2 landed, 3-6 proposed): + +| Slice | Status | Surface that conflicts with our PRs | +|-------|--------|-------------------------------------| +| `workspace-foundation` | landed | `instructions.ts`, `templates.ts`, `instruction-loader.ts`, `profiles.ts` — already merged into fork via sync/upstream-v1.3.1 | +| `workspace-create-and-register-repos` | landed (CLI beta) | Setup CLI surface — no overlap with our PRs | +| `workspace-open-agent-context` | proposed | Context injection — overlaps T1's plugin-loaded skill content | +| `workspace-change-planning` | proposed | New change creation — neutral | +| `workspace-apply-repo-slice` | proposed | **`apply` redefined as repo-slice implementation** — directly conflicts with T4's `openspec run start/complete` model | +| `workspace-verify-and-archive` | proposed | **verify/archive at workspace + repo level** — directly conflicts with T2's phase extension model | + +**Strategic implications:** + +1. **T1 (Plugin Foundation)** — surface already churned by foundation slice. Rebase will be expensive but achievable. Submit early before slice 3 lands and adds another layer. + +2. **T2 (Workflow Phases)** — verify/archive phase types we want to add will likely be redefined by slice 6. Two paths: + - (a) Submit T2 anyway, position as "phase machinery that slice 6 can build on" + - (b) Wait for slice 6, then merge our verify/archive intent into the workspace model + - Default: (b) — saves rework, signals respect for upstream direction. + +3. **T4 (Pipeline Runner)** — `apply` semantics redefined by slice 5. `openspec run start/complete` would need to know whether it's running on a workspace change or a repo-local change. Defer until slice 5 stabilizes. + +4. **T3 (Orchestration Hints)** — workspace-neutral; flags affect agent dispatch regardless of change scope. Lower-risk; can ship after T1. + +5. **T5 (Fingerprinting)** — gate-checker internal, no workspace coupling. Independent. + +**CORE_WORKFLOWS union** (sync/upstream-v1.3.1 merge resolution): both `verify` (fork) and `sync` (upstream workspace) are now default-active. T2 PR upstream must reckon with this — either remove `verify` from CORE if upstream prefers minimal default, or argue for keeping both. This is a foreseeable point of friction. + +### AD-3: Odoo-specific content exclusion + +The following content exists in the fork but MUST NOT be included in any upstream PR: + +| Content | Location | Reason | +|---------|----------|--------| +| `schemas/odoo-sdd/` | Deleted from fork, lives in odoo-claude-code | Domain-specific | +| `schemas/odoo-workflow/` | Deleted from fork, lives in odoo-claude-code | Domain-specific | +| Odoo change proposals | `openspec/changes/` | Project-specific | +| `openspec-parallel-merge-plan.md` | Root | Fork-specific planning doc | + +### AD-4: PR dependency chain + +``` +T0 (icebreaker) ── no code dependency + │ + T1 (plugin foundation) + │ exports: PluginManifest, PluginLoader, HookDispatcher, ConfigValidator + │ new CLI: openspec plugin list|info + │ + ├── T2 (workflow phases) ── depends on T1 schema types + │ extends: SchemaYaml with verify/archive phases + │ + ├── T3 (orchestration) ── depends on T1 plugin loader + │ new CLI flags: --subagents, --teams, --sequential + │ new module: src/core/orchestration/ + │ + │ └── T4 (pipeline runner) ── depends on T1 + T3 + │ new CLI: openspec run, openspec gate + │ new module: src/core/pipeline/ + │ + └── T5 (fingerprinting) ── weak dependency on T4 gate-checker + extends: gate-checker with content fingerprints +``` + +## Commit-to-PR Mapping + +### T1: Plugin Foundation +| Fork Commit | Description | +|-------------|-------------| +| `a3a0270` | Plugin system core (loader, hooks, config, gates, schemas) | +| `a037ec5` | Skill overlays injection | +| `6ba2731` | README plugin documentation | +| `354c7f2` | Archive add-skill-overlays change | +| `fbec042` | Fix plugin-provided schema resolution | +| `9446dbd` | Fix loadTemplate plugin schema resolution | + +### T2: Workflow Phase Extension +| Fork Commit | Description | +|-------------|-------------| +| `11c22d2` | Configurable changesDir | +| `63cae69` | Verify and archive phase schema support | + +### T3: Orchestration Hints +| Fork Commit | Description | +|-------------|-------------| +| `34af9ac` | CLI-native orchestration hints | +| `c552889` | Schema default_mode and --sequential flag | + +### T4: Pipeline Runner +| Fork Commit | Description | +|-------------|-------------| +| `a324c3c` | Pipeline runner (runner, lock, types) | +| `5e0c7db` | Fix hook command CWD resolution | +| `df81ea0` | Covers auto-injection, change class routing | + +### T5: Fingerprinting +| Fork Commit | Description | +|-------------|-------------| +| `375ce99` | Content-based fingerprinting for gate staleness | + +### Not included in any PR (Odoo-specific or fork-only) +| Fork Commit | Description | Reason | +|-------------|-------------|--------| +| `acea753` | odoo-workflow schema | Odoo-specific | +| `39f5636` | Lifecycle addon for odoo-workflow | Odoo-specific | +| `c0f249b` | Align odoo-sdd schemas | Odoo-specific | +| `1b0cc82` | Schema-level quality gates + odoo-sdd | Mixed (gates go in T1, odoo-sdd stays) | +| `4c762cc` | 63 tests for gates | Goes with T1/T4 | +| `9b3818e` | Test fixes for verify addition | Goes with T2 | +| `13bb4e9` | ZshInstaller test isolation | Goes with T1 | +| `22dde36` | Strengthen spec template format | Goes with T2 | +| `778f1a5` | Ignore .actrc | Already in upstream via rebase | +| `2c92b77` | Remove Odoo plugins from dev config | Fork cleanup | + +## Per-PR Checklist + +For each PR before submission: + +1. [ ] Create branch from latest upstream/main +2. [ ] Cherry-pick relevant commits +3. [ ] Remove any Odoo-specific content that leaked in +4. [ ] Ensure all new exports are properly typed +5. [ ] Run `npm run build` — clean compile +6. [ ] Run `npx vitest run` — only pre-existing failures allowed +7. [ ] Write PR description with context for upstream reviewers +8. [ ] Cross-reference any relevant upstream issues (#879, #920, etc.) diff --git a/openspec/changes/upstream-contribution/proposal.md b/openspec/changes/upstream-contribution/proposal.md new file mode 100644 index 000000000..217cfded0 --- /dev/null +++ b/openspec/changes/upstream-contribution/proposal.md @@ -0,0 +1,64 @@ +# Upstream Contribution: Plugin System & Orchestration Platform + +## Problem + +OpenSpec is currently a static spec-driven development tool. It generates instruction files and manages change artifacts, but lacks: + +1. **Extensibility** — No way for teams to add custom lifecycle hooks, quality gates, or domain-specific schemas without forking +2. **Quality assurance** — No automated validation that artifacts (proposal, specs, design, tasks) are aligned and traceable +3. **Orchestration** — No mechanism to express parallel/sequential execution preferences for multi-agent workflows +4. **Pipeline execution** — No runtime for automatically executing gates and hooks at lifecycle boundaries + +This fork has developed and production-tested these capabilities over 3 months of daily use in an Odoo 18 Enterprise development environment. + +## Proposed Solution + +Contribute the fork's 25 commits to upstream Fission-AI/OpenSpec as 5 sequential PRs, preceded by a Phase 0 icebreaker engagement. Each PR builds on the previous, and we wait for merge before opening the next. + +### PR Sequence (revised 2026-05-04 after workspace foundation landed) + +Upstream has shifted priority to a **cross-repo workspace model** (workspace-foundation merged in PR #1045 on 2026-05-04). Workspace slice 3–6 proposals overlap with our T2/T4 surface area. The original sequential plan is preserved below for context, but execution order and risk have been reassessed. + +| PR | Name | New order | Risk | Workspace overlap | +|----|------|-----------|------|-------------------| +| T0 | Icebreaker | DONE (PR #891 comment, TabishB ack 04-13, then 21+ days silent) | — | n/a | +| **T1** | Plugin Foundation | **submit first** | **HIGH** (was MEDIUM) | Touches `init.ts/templates.ts/instructions.ts` — same surface workspace-foundation just refactored | +| **T5** | Fingerprinting | **submit in parallel with T1** | LOW | None — gate-checker internal | +| **T3** | Orchestration Hints | submit after T1 lands | LOW (was MEDIUM) | Cross-cutting, workspace-neutral | +| T2 | Workflow Phase Extension (verify/archive) | **defer or reframe** | MEDIUM | **Direct overlap** with `workspace-verify-and-archive` slice 6 (proposed). Either reframe as "phase mechanism for slice 6" or fold in. | +| T4 | Pipeline Runner | **defer until slice 5 lands** | HIGH | **Direct overlap** with `workspace-apply-repo-slice` slice 5 (proposed). `apply` semantics being redefined per repo-slice; T4's `openspec run start/complete` would need workspace-awareness. | + +**Why this order**: T1 + T5 + T3 ship the broad-utility primitives; T2/T4 are deferred until upstream commits to a workspace-friendly seam, otherwise we waste rebase effort fighting the wrong abstraction. + +### What stays internal (NOT contributed) + +- Odoo-specific schemas (odoo-sdd, odoo-bugfix, odoo-trivial, odoo-refactor) +- alignment-check, dual-review, code-quality plugins (kept as independent examples) +- Obsidian vault sync hooks +- Git branch/worktree policy hooks + +## Success Criteria + +- All 5 PRs merged into Fission-AI/OpenSpec main +- Upstream tests pass (excluding pre-existing `@inquirer/core` issue) +- No Odoo-specific code in upstream +- Plugin system documented in upstream README + +## Constraints + +- Conservative strategy: one PR at a time, wait for merge +- Each PR must rebase onto latest upstream/main (which includes previous merged PR) +- Upstream maintainer (TabishB) has ~24-48h review turnaround +- Estimated total timeline: 3-6 weeks +- `contextFiles` type changed from `string` to `string[]` in v1.3.0 — all fork code adapted during rebase + +## Risks + +| Risk | Mitigation | +|------|-----------| +| Upstream rejects plugin architecture | Phase 0 done (TabishB ack 04-13); silence since then is risk indicator — push T1 to force a review | +| Workspace path eats T2/T4 surface | Defer T2/T4; reframe as workspace-aware after slice 5/6 stabilize | +| API changes requested after PR1 | Each subsequent PR rebases from merged upstream, adapts as needed | +| Long review cycles | Work on other projects during wait; conservative strategy reduces rework | +| Workspace foundation blocks plugin auto-discovery for cross-repo | T1 must explicitly state "plugin-foundation is repo-local; workspace-level plugins are out of scope here" | +| Maintainer attention saturated by workspace path | Submit T1 + T5 with explicit "complementary, not competing" framing; reference slice 1/2 architecture in PR body | diff --git a/openspec/changes/upstream-contribution/tasks.md b/openspec/changes/upstream-contribution/tasks.md new file mode 100644 index 000000000..be1cf23a8 --- /dev/null +++ b/openspec/changes/upstream-contribution/tasks.md @@ -0,0 +1,97 @@ +# Tasks: Upstream Contribution + +## 0. Icebreaker — Establish trust with upstream maintainer + +- [x] Review open P0 issues (#879, #881, #920) and identify one where plugin system insight is relevant +- [x] Leave a thoughtful comment explaining how the fork's plugin architecture addresses the issue + - Posted on PR #891: https://github.com/Fission-AI/OpenSpec/pull/891#issuecomment-4234369497 + - Connected to TabishB's "unify-template-generation-pipeline" vision +- [x] Maintainer ack — 2026-04-13: TabishB replied "definitely be interested, let me take a look and get back to you" +- [x] **Status (2026-05-04)**: 21 days silence. TabishB's attention shifted to workspace foundation (PR #1045 merged 05-04). Treat as **stalled, not negative** — push forward with T1 to force review. + +## 0.5. Sync upstream/main into fork (2026-05-04) + +- [x] Merge `upstream/main` into `sync/upstream-v1.3.1` branch + - 9 conflict files, 32 hunks; 4 real semantic decisions (CORE_WORKFLOWS union, instruction-loader nesting, etc.) +- [x] All tests green: 1719/1719 +- [x] PR opened: https://github.com/stanleykao72/OpenSpec/pull/12 +- [x] Update this umbrella with workspace-aware revisions (this commit) + +## 1. PR: Plugin Foundation (T1) — submit FIRST + +Commits: a3a0270, a037ec5, 6ba2731, 354c7f2, fbec042, 9446dbd + +**Risk: HIGH** — surface area was just refactored by workspace-foundation (PR #1045). Rebase from latest upstream/main (after #1045 merge) and expect conflict resolution work in `init.ts/templates.ts/instructions.ts/instruction-loader.ts`. Reference sync/upstream-v1.3.1 PR #12 in fork as proof-of-concept that conflicts are tractable. + +- [ ] Create branch `feat/plugin-system` from latest upstream/main (post-#1045) +- [ ] Cherry-pick and adapt commits (remove Odoo references) +- [ ] Verify: `src/core/plugin/` (types, loader, hook-dispatcher, config-validator, context) +- [ ] Verify: `src/commands/plugin.ts` (list, info subcommands) +- [ ] Verify: skill overlay injection in `src/core/update.ts` +- [ ] Verify: schema resolution supports plugin-bundled schemas +- [ ] Include `schemas/spec-driven/schema.yaml` as default example +- [ ] PR body: cite sync PR #12 conflict resolution; explicitly state "plugin-foundation is repo-local; workspace-level plugins are out of scope" +- [ ] Run build + tests +- [ ] `gh pr create -R Fission-AI/OpenSpec` +- [ ] Record PR URL: ___ +- [ ] Track review feedback and iterate +- [ ] Merged: [ ] + +## 2. PR: Fingerprinting & Staleness Detection (T5) — submit IN PARALLEL with T1 + +Commits: 375ce99 + +**Risk: LOW** — gate-checker internal, no workspace coupling. Submit in parallel with T1 (no dependency). + +- [ ] Create branch `feat/gate-fingerprinting` from latest upstream/main +- [ ] Cherry-pick: content-based fingerprinting +- [ ] Verify: SHA256 fingerprinting of tracked artifacts +- [ ] Verify: stale gate detection in gate synthesis +- [ ] Run build + tests +- [ ] `gh pr create -R Fission-AI/OpenSpec` +- [ ] Record PR URL: ___ +- [ ] Merged: [ ] + +## 3. PR: Orchestration Hints (T3) — submit AFTER T1 lands + +Commits: 34af9ac, c552889 + +**Risk: LOW** (downgraded from MEDIUM) — workspace-neutral cross-cutting flags. + +- [ ] Rebase from upstream/main (now includes T1) +- [ ] Create branch `feat/orchestration-hints` +- [ ] Cherry-pick: CLI-native orchestration, default_mode, --sequential +- [ ] Verify: `src/core/orchestration/` (types, resolver, domain-parser, group-builder) +- [ ] Verify: --subagents, --teams, --sequential CLI flags +- [ ] Run build + tests +- [ ] `gh pr create -R Fission-AI/OpenSpec` +- [ ] Record PR URL: ___ +- [ ] Merged: [ ] + +## 4. PR: Workflow Phase Extension (T2) — DEFERRED + +Commits: 11c22d2, 63cae69, 22dde36, 9b3818e + +**Risk: MEDIUM** — overlaps with `workspace-verify-and-archive` slice 6 (proposed). Default plan: wait for slice 6 to land, then refactor T2 into workspace-aware verify/archive support OR fold our intent into upstream's design. + +Decision gates before submitting: +- [ ] Has slice 6 (workspace-verify-and-archive) landed in upstream? + - If yes → re-pitch T2 as a workspace-aware addition (or skip if slice 6 covers our use case) + - If no after 60+ days → submit T2 anyway as "phase mechanism for slice 6"; cite slice 6 proposal in PR body +- [ ] CORE_WORKFLOWS friction: be ready to argue for keeping `verify` in CORE alongside upstream's `sync` + +(Sub-tasks unchanged when execution time arrives — see prior section content.) + +## 5. PR: Pipeline Runner + Gates/Hooks Execution (T4) — DEFERRED + +Commits: a324c3c, 5e0c7db, df81ea0, 1b0cc82 (gates portion), 4c762cc, 13bb4e9 + +**Risk: HIGH** — `apply` semantics being redefined by slice 5 `workspace-apply-repo-slice` (proposed). `openspec run start/complete` must reckon with workspace vs repo-local change scope. Hard to ship before slice 5 lands. + +Decision gates before submitting: +- [ ] Has slice 5 (workspace-apply-repo-slice) landed? + - If yes → refactor T4's runner to be workspace-aware; gate-checker reads from workspace `changes/` if applicable + - If no after 90+ days → submit T4 anyway as "pipeline mechanism for slice 5"; offer to add workspace-aware path in follow-up +- [ ] Verify gate-checker remains decoupled from `apply` semantics (so slice 5 doesn't force gate-checker rewrite) + +(Sub-tasks unchanged when execution time arrives — see prior section content.)