diff --git a/docs/sops/SOP-101-frame-lifecycle.md b/docs/sops/SOP-101-frame-lifecycle.md new file mode 100644 index 00000000..2994a6da --- /dev/null +++ b/docs/sops/SOP-101-frame-lifecycle.md @@ -0,0 +1,32 @@ +# SOP-101 Frame Lifecycle + +**Owner:** Platform Team +**Status:** Active +**Related PROSE Expectation:** [E.1 Frame stack integrity](../specs/PROSE-platform-overview.md#e1-frame-stack-integrity) + +## Objective +Ensure context frames are created, activated, and closed in a deterministic, non-circular order so that only one frame is active at a time. + +## Procedure + +1. **Frame creation** + - A frame is created with a unique name, type, and optional parent frame. + - The newly created frame becomes the active frame. + +2. **Frame activation** + - Only the most recently pushed frame may be active. + - Previous frames remain in the stack but are inactive. + +3. **Frame closure** + - Closing a frame removes it from the top of the stack. + - The previous frame automatically becomes active. + - No frame may reference itself as a parent, directly or transitively. + +## Verification + +- Run integration test: `active frame stack remains consistent` +- Expected result: after pushing frames A → B → C and popping twice, only A remains active. + +## Non-compliance + +Orphaned active frames, circular parent references, or multiple simultaneous active frames are considered non-compliant and must fail validation. diff --git a/docs/sops/SOP-102-decision-record-keeping.md b/docs/sops/SOP-102-decision-record-keeping.md new file mode 100644 index 00000000..690d8b94 --- /dev/null +++ b/docs/sops/SOP-102-decision-record-keeping.md @@ -0,0 +1,30 @@ +# SOP-102 Decision Record Keeping + +**Owner:** Platform Team +**Status:** Active +**Related PROSE Expectation:** [E.2 Decision immutability](../specs/PROSE-platform-overview.md#e2-decision-immutability) + +## Objective +Preserve decisions as tamper-evident audit records so that historical rationale remains trustworthy across sessions. + +## Procedure + +1. **Recording a decision** + - Capture title, rationale, timestamp, and author context. + - Assign a stable identifier at creation time. + +2. **Storage** + - Store the decision in the project-local SQLite database. + - Do not overwrite existing decision records. + +3. **Retrieval** + - Decisions must be retrievable by identifier, title, or full-text search. + +## Verification + +- Run integration test: `recorded decisions are immutable` +- Expected result: re-adding a decision with the same title creates a new record; the original rationale remains unchanged. + +## Non-compliance + +Mutating an existing decision's title, rationale, or identifier is non-compliant. diff --git a/docs/sops/SOP-103-project-boundary-enforcement.md b/docs/sops/SOP-103-project-boundary-enforcement.md new file mode 100644 index 00000000..daa109da --- /dev/null +++ b/docs/sops/SOP-103-project-boundary-enforcement.md @@ -0,0 +1,30 @@ +# SOP-103 Project Boundary Enforcement + +**Owner:** Platform Team +**Status:** Active +**Related PROSE Expectation:** [E.3 Project isolation](../specs/PROSE-platform-overview.md#e3-project-isolation) + +## Objective +Prevent context leakage between projects so that each project directory owns an independent memory store. + +## Procedure + +1. **Project initialization** + - Create a `.stackmemory/` directory inside the target project directory. + - Store all project-specific data within that directory. + +2. **Operation scoping** + - Resolve the active project by the current working directory. + - Do not search parent directories for sibling project stores. + +3. **Cross-project sync** + - Context may only be shared between projects through explicit, user-initiated sync commands. + +## Verification + +- Run integration test: `projects in different directories are isolated` +- Expected result: a decision recorded in project A is not visible in project B. + +## Non-compliance + +Reading or writing another project's data without explicit sync is non-compliant. diff --git a/docs/sops/SOP-201-cli-exit-code-compliance.md b/docs/sops/SOP-201-cli-exit-code-compliance.md new file mode 100644 index 00000000..38fb6514 --- /dev/null +++ b/docs/sops/SOP-201-cli-exit-code-compliance.md @@ -0,0 +1,29 @@ +# SOP-201 CLI Exit-Code Compliance + +**Owner:** CLI Team +**Status:** Active +**Related PROSE Expectation:** [E.4 CLI contract](../specs/PROSE-platform-overview.md#e4-cli-contract) + +## Objective +Provide deterministic exit codes so that scripts, CI pipelines, and automation tools can rely on CLI outcomes. + +## Procedure + +1. **Success** + - Return exit code `0` when a command completes its intended function. + +2. **Failure** + - Return a non-zero exit code when a command cannot complete its intended function. + - Include a human-readable error message on stderr or stdout. + +3. **Documentation** + - Document exit-code behavior for commands used in automation. + +## Verification + +- Run integration test: `CLI commands return correct exit codes` +- Expected result: successful commands exit `0`; invalid commands exit non-zero. + +## Non-compliance + +Returning `0` on failure or a non-zero code on success is non-compliant. diff --git a/docs/sops/SOP-202-data-portability.md b/docs/sops/SOP-202-data-portability.md new file mode 100644 index 00000000..c16e0800 --- /dev/null +++ b/docs/sops/SOP-202-data-portability.md @@ -0,0 +1,30 @@ +# SOP-202 Data Portability + +**Owner:** Platform Team +**Status:** Active +**Related PROSE Expectation:** [E.5 SQLite contract](../specs/PROSE-platform-overview.md#e5-sqlite-contract) + +## Objective +Ensure the project memory store is self-contained and portable across machines without external dependencies. + +## Procedure + +1. **Self-contained storage** + - Keep the primary database file inside `.stackmemory/`. + - Avoid reliance on remote databases for core context storage. + +2. **Schema compatibility** + - Maintain forward-compatible schemas within major CLI versions. + - Document migration steps when schema changes are required. + +3. **Backup and restore** + - A user must be able to copy `.stackmemory/` to another machine and resume work with the same CLI version. + +## Verification + +- Run integration test: `SQLite database is self-contained in .stackmemory` +- Expected result: after initialization and recording context, a `.db` or `.sqlite` file exists under `.stackmemory/`. + +## Non-compliance + +Requiring external services for basic context retrieval or scattering state outside `.stackmemory/` is non-compliant. diff --git a/docs/specs/PROSE-platform-overview.md b/docs/specs/PROSE-platform-overview.md index a394c249..32cbd40c 100644 --- a/docs/specs/PROSE-platform-overview.md +++ b/docs/specs/PROSE-platform-overview.md @@ -1,9 +1,13 @@ # PROSE Platform Overview > PROSE = **P**urpose, **R**ules & Constraints, **O**bservables, **S**cenarios, **E**xpectations +> +> Expectations are grounded in **SOPs** — Standard Operating Procedures that define repeatable business outcomes and standardized outputs. This document describes StackMemory's platform behavior in plain English. Each section is intentionally testable and maps directly to integration tests in `src/__tests__/integration/platform-overview.test.ts`. +The contract layer of PROSE is derived from SOPs: every Expectation below corresponds to a procedural guarantee that QA can validate and that can be turned into executable scripts. + --- ## P — Purpose @@ -71,44 +75,73 @@ Capturing a snapshot persists the current context state for later handoff. ## E — Expectations -Properties that must always hold true and guarantees the platform makes to users and integrations. +Properties that must always hold true and guarantees the platform makes to users and integrations. Each Expectation is backed by one or more SOPs so that business outcomes can be standardized, QA'd, and automated. ### E.1 Frame stack integrity At any time, there is at most one active frame, and the frame stack is non-circular. +**SOP basis:** `SOP-101 Frame Lifecycle` — frames must be pushed and popped in a deterministic order; no orphaned active frames are allowed. + ### E.2 Decision immutability Once recorded, a decision's identifier, title, and rationale must not change. +**SOP basis:** `SOP-102 Decision Record Keeping` — decisions are audit records and must remain tamper-evident. + ### E.3 Project isolation Two projects initialized in different directories must not share context unless explicitly synced. +**SOP basis:** `SOP-103 Project Boundary Enforcement` — each project directory owns its SQLite database and must not leak state into neighboring projects. + ### E.4 CLI contract All CLI commands return a zero exit code on success and a non-zero exit code on failure, with human-readable output. +**SOP basis:** `SOP-201 CLI Exit-Code Compliance` — scripts and CI pipelines depend on reliable exit codes. + ### E.5 SQLite contract The local SQLite database is self-contained within `.stackmemory/` and portable across machines with the same CLI version. +**SOP basis:** `SOP-202 Data Portability` — the database must be relocatable and restorable without external services. + +--- + +## SOP → PROSE → Tests workflow + +``` +SOP (business outcome) + │ + ▼ +PROSE Expectation (plain-English contract) + │ + ▼ +Integration test (executable validation) + │ + ▼ +Feature-script parity (automation) +``` + +For example, `SOP-101 Frame Lifecycle` becomes PROSE `E.1`, which becomes the test `active frame stack remains consistent`. QA can run the same test suite to verify SOP compliance, and the test can be used to generate or validate automation scripts. + --- ## Test mapping -| PROSE ID | Test case | -|----------|-----------| -| P.1 | `initializes a project with stackmemory init` | -| P.2 | `retrieves decisions across sessions` | -| R.1 | `fails gracefully outside an initialized project` | -| R.2 | `returns empty results for non-matching search` | -| R.3 | `init is idempotent` | -| O.1 | `reports status for initialized and uninitialized projects` | -| O.2 | `lists pushed frames` | -| O.3 | `lists recorded decisions` | -| O.4 | `searches stored context` | -| S.1 | `pushing a frame creates a scoped entry` | -| S.2 | `popping a frame restores the previous frame` | -| S.3 | `recording a decision persists rationale` | -| S.4 | `capturing a snapshot persists handoff state` | -| E.1 | `active frame stack remains consistent` | -| E.2 | `recorded decisions are immutable` | -| E.3 | `projects in different directories are isolated` | -| E.4 | `CLI commands return correct exit codes` | -| E.5 | `SQLite database is self-contained in .stackmemory` | +| PROSE ID | SOP basis | Test case | +|----------|-----------|-----------| +| P.1 | — | `initializes a project with stackmemory init` | +| P.2 | — | `retrieves decisions across sessions` | +| R.1 | — | `fails gracefully outside an initialized project` | +| R.2 | — | `returns empty results for non-matching search` | +| R.3 | — | `init is idempotent` | +| O.1 | — | `reports status for initialized and uninitialized projects` | +| O.2 | — | `lists pushed frames` | +| O.3 | — | `lists recorded decisions` | +| O.4 | — | `searches stored context` | +| S.1 | — | `pushing a frame creates a scoped entry` | +| S.2 | — | `popping a frame restores the previous frame` | +| S.3 | — | `recording a decision persists rationale` | +| S.4 | — | `capturing a snapshot persists handoff state` | +| E.1 | `SOP-101 Frame Lifecycle` | `active frame stack remains consistent` | +| E.2 | `SOP-102 Decision Record Keeping` | `recorded decisions are immutable` | +| E.3 | `SOP-103 Project Boundary Enforcement` | `projects in different directories are isolated` | +| E.4 | `SOP-201 CLI Exit-Code Compliance` | `CLI commands return correct exit codes` | +| E.5 | `SOP-202 Data Portability` | `SQLite database is self-contained in .stackmemory` | diff --git a/src/__tests__/integration/platform-overview.test.ts b/src/__tests__/integration/platform-overview.test.ts index 7a8fee4e..c1b8ef4f 100644 --- a/src/__tests__/integration/platform-overview.test.ts +++ b/src/__tests__/integration/platform-overview.test.ts @@ -263,9 +263,13 @@ describe('PROSE Platform Overview', { timeout: 60_000 }, () => { // --------------------------------------------------------------------------- // E — Expectations + // + // Each Expectation is grounded in an SOP (Standard Operating Procedure) so + // that business outcomes can be standardized, QA'd, and automated. + // SOP source: docs/sops/ // --------------------------------------------------------------------------- - describe('E.1 Frame stack integrity', () => { + describe('E.1 Frame stack integrity (SOP-101)', () => { it('active frame stack remains consistent', () => { run('init', testDir); run('context push "Alpha frame"', testDir); @@ -282,7 +286,7 @@ describe('PROSE Platform Overview', { timeout: 60_000 }, () => { }); }); - describe('E.2 Decision immutability', () => { + describe('E.2 Decision immutability (SOP-102)', () => { it('recorded decisions are immutable', () => { run('init', testDir); run( @@ -304,7 +308,7 @@ describe('PROSE Platform Overview', { timeout: 60_000 }, () => { }); }); - describe('E.3 Project isolation', () => { + describe('E.3 Project isolation (SOP-103)', () => { it('projects in different directories are isolated', () => { const projectA = path.join(testDir, 'project-a'); const projectB = path.join(testDir, 'project-b'); @@ -322,7 +326,7 @@ describe('PROSE Platform Overview', { timeout: 60_000 }, () => { }); }); - describe('E.4 CLI contract', () => { + describe('E.4 CLI contract (SOP-201)', () => { it('CLI commands return correct exit codes', () => { expect(() => run('init', testDir)).not.toThrow(); expect(() => run('decision list', testDir)).not.toThrow(); @@ -332,7 +336,7 @@ describe('PROSE Platform Overview', { timeout: 60_000 }, () => { }); }); - describe('E.5 SQLite contract', () => { + describe('E.5 SQLite contract (SOP-202)', () => { it('SQLite database is self-contained in .stackmemory', () => { run('init', testDir); run('decision add "DB test" --why "Check local DB"', testDir);