Skip to content

Commit e9518ff

Browse files
author
StackMemory Bot (CLI)
committed
chore: misc updates — PROSE platform overview, provenant compliance, scaffold cmd, wiki compiler
1 parent 2b8fbe8 commit e9518ff

5 files changed

Lines changed: 278 additions & 11 deletions

File tree

docs/specs/PROSE-platform-overview.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,106 @@ The local SQLite database is self-contained within `.stackmemory/` and portable
102102

103103
**SOP basis:** `SOP-202 Data Portability` — the database must be relocatable and restorable without external services.
104104

105+
### E.6 Error Handling contract
106+
Errors are surfaced through structured responses with sufficient context for callers to recover or retry.
107+
108+
**SOP basis:** `SOP-1601 Circuit Breaking` and generated error-handling SOPs — failures must be isolated and observable.
109+
110+
### E.7 Observability contract
111+
The platform emits metrics, logs, or traces that make internal state visible to operators.
112+
113+
**SOP basis:** Generated observability SOPs — state changes must be observable without relying on ad-hoc inspection.
114+
115+
### E.8 Backup and Recovery contract
116+
User data can be exported and restored within the same major CLI version without external services.
117+
118+
**SOP basis:** Generated backup-and-recovery SOPs — data loss scenarios must have a documented recovery path.
119+
120+
### E.9 Authentication contract
121+
Actions that modify project state are attributable to an authenticated actor.
122+
123+
**SOP basis:** Generated authentication SOPs — identity must be established before privileged operations.
124+
125+
### E.10 Authorization contract
126+
Access to project context is enforced according to the actor's permissions.
127+
128+
**SOP basis:** Generated authorization SOPs — unauthorized access to project memory is non-compliant.
129+
130+
### E.11 Data Encryption contract
131+
Sensitive data at rest is encrypted using platform-standard algorithms.
132+
133+
**SOP basis:** Generated data-encryption SOPs — plaintext storage of secrets or credentials is non-compliant.
134+
135+
### E.12 Input Validation contract
136+
External input is validated before processing to prevent injection or corruption.
137+
138+
**SOP basis:** Generated input-validation SOPs — invalid input must be rejected at the boundary.
139+
140+
### E.13 Rate Limiting contract
141+
API and CLI operations respect configured rate limits to prevent abuse or overload.
142+
143+
**SOP basis:** Generated rate-limiting SOPs — exceeding configured limits must be throttled.
144+
145+
### E.14 Audit Logging contract
146+
Security-relevant actions are recorded in an append-only audit log.
147+
148+
**SOP basis:** Generated audit-logging SOPs — privileged actions must leave a traceable record.
149+
150+
### E.15 Dependency Management contract
151+
External dependencies are tracked and scanned for known vulnerabilities.
152+
153+
**SOP basis:** Generated dependency-management SOPs — unmanaged dependencies are non-compliant.
154+
155+
### E.16 Secret Rotation contract
156+
Credentials and secrets are rotated on a defined schedule.
157+
158+
**SOP basis:** Generated secret-rotation SOPs — expired or stale secrets must be replaced.
159+
160+
### E.17 Multi-Agent Coordination contract
161+
Concurrent agents operating on the same project do not corrupt shared state.
162+
163+
**SOP basis:** Generated multi-agent-coordination SOPs — conflicting writes must be resolved safely.
164+
165+
### E.18 API Versioning contract
166+
Public API changes preserve backward compatibility within a major version.
167+
168+
**SOP basis:** Generated API-versioning SOPs — breaking changes must be versioned explicitly.
169+
170+
### E.19 Configuration Management contract
171+
Configuration is validated at load time and changes are traceable.
172+
173+
**SOP basis:** Generated configuration-management SOPs — invalid or untracked config is non-compliant.
174+
175+
### E.20 Schema Migration contract
176+
Database schema changes are forward-compatible and reversible within a major version.
177+
178+
**SOP basis:** Generated schema-migration SOPs — migrations must not destroy user data.
179+
180+
### E.21 Performance Budget contract
181+
Operations complete within defined latency and resource limits.
182+
183+
**SOP basis:** Generated performance-budget SOPs — consistent budget violations are non-compliant.
184+
185+
### E.22 Caching Strategy contract
186+
Cached data is invalidated or refreshed when underlying state changes.
187+
188+
**SOP basis:** Generated caching-strategy SOPs — stale cache reads are non-compliant.
189+
190+
### E.23 Retry Policy contract
191+
Transient failures are retried with backoff and jitter before surfacing as errors.
192+
193+
**SOP basis:** Generated retry-policy SOPs — unbounded or immediate retries are non-compliant.
194+
195+
### E.24 Circuit Breaking contract
196+
Repeated failures trigger circuit breaking to prevent cascading overload.
197+
198+
**SOP basis:** `SOP-1601 Circuit Breaking` and generated circuit-breaking SOPs — the circuit must open after configured thresholds.
199+
200+
### E.25 Feature Flagging contract
201+
Feature flags are evaluated consistently and changes are auditable.
202+
203+
**SOP basis:** Generated feature-flagging SOPs — flag state must be deterministic and traceable.
204+
105205
---
106206

107207
## SOP → PROSE → Tests workflow
@@ -145,3 +245,23 @@ For example, `SOP-101 Frame Lifecycle` becomes PROSE `E.1`, which becomes the te
145245
| E.3 | `SOP-103 Project Boundary Enforcement` | `projects in different directories are isolated` |
146246
| E.4 | `SOP-201 CLI Exit-Code Compliance` | `CLI commands return correct exit codes` |
147247
| E.5 | `SOP-202 Data Portability` | `SQLite database is self-contained in .stackmemory` |
248+
| E.6 | `SOP-1xxx Error Handling` | `errors are surfaced as structured responses` |
249+
| E.7 | `SOP-1xxx Observability` | `platform emits observable metrics/logs/traces` |
250+
| E.8 | `SOP-1xxx Backup and Recovery` | `data can be exported and restored` |
251+
| E.9 | `SOP-1xxx Authentication` | `modifications are attributable to an actor` |
252+
| E.10 | `SOP-1xxx Authorization` | `access is enforced by permission` |
253+
| E.11 | `SOP-1xxx Data Encryption` | `sensitive data at rest is encrypted` |
254+
| E.12 | `SOP-1xxx Input Validation` | `invalid input is rejected at the boundary` |
255+
| E.13 | `SOP-1xxx Rate Limiting` | `operations respect configured rate limits` |
256+
| E.14 | `SOP-1xxx Audit Logging` | `security actions are append-only logged` |
257+
| E.15 | `SOP-1xxx Dependency Management` | `dependencies are tracked and scanned` |
258+
| E.16 | `SOP-1xxx Secret Rotation` | `secrets are rotated on schedule` |
259+
| E.17 | `SOP-1xxx Multi-Agent Coordination` | `concurrent agents do not corrupt state` |
260+
| E.18 | `SOP-1xxx API Versioning` | `public API changes preserve compatibility` |
261+
| E.19 | `SOP-1xxx Configuration Management` | `config is validated and traceable` |
262+
| E.20 | `SOP-1xxx Schema Migration` | `schema changes are forward-compatible` |
263+
| E.21 | `SOP-1xxx Performance Budget` | `operations stay within latency/resource limits` |
264+
| E.22 | `SOP-1xxx Caching Strategy` | `cache is invalidated on state change` |
265+
| E.23 | `SOP-1xxx Retry Policy` | `transient failures retry with backoff` |
266+
| E.24 | `SOP-1601 Circuit Breaking` | `circuit opens after failure thresholds` |
267+
| E.25 | `SOP-1xxx Feature Flagging` | `flag evaluation is deterministic and auditable` |

packages/provenant/src/cli/commands/compliance.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ interface ComplianceEntry {
1717

1818
export function compliance(opts: ComplianceOpts): void {
1919
if (!existsSync(opts.db)) {
20-
console.error('No database found. Run `provenant log-decision` or ingest first.');
20+
console.error(
21+
'No database found. Run `provenant log-decision` or ingest first.'
22+
);
2123
process.exit(1);
2224
}
2325

@@ -42,13 +44,19 @@ export function compliance(opts: ComplianceOpts): void {
4244
// ignore malformed payload
4345
}
4446

45-
const proseId = (metadata['proseId'] as string) ?? extractProseId(node.content);
47+
const proseId =
48+
(metadata['proseId'] as string) ?? extractProseId(node.content);
4649
const sop = (metadata['sop'] as string) ?? extractSop(node.content);
4750
if (!proseId || seen.has(proseId)) continue;
4851
seen.add(proseId);
4952

50-
const passed = node.content.includes('compliance verified');
51-
const failed = node.content.includes('compliance NOT verified');
53+
const metadataStatus = metadata['status'] as string | undefined;
54+
const passed =
55+
metadataStatus === 'passed' ||
56+
node.content.includes('compliance verified');
57+
const failed =
58+
metadataStatus === 'failed' ||
59+
node.content.includes('compliance NOT verified');
5260

5361
entries.push({
5462
proseId,
@@ -76,14 +84,17 @@ export function compliance(opts: ComplianceOpts): void {
7684
const failed = entries.filter((e) => e.status === 'fail').length;
7785

7886
for (const entry of entries) {
79-
const icon = entry.status === 'pass' ? '✓' : entry.status === 'fail' ? '✗' : '?';
87+
const icon =
88+
entry.status === 'pass' ? '✓' : entry.status === 'fail' ? '✗' : '?';
8089
console.log(
8190
`${icon} ${entry.proseId} ${entry.sop.slice(0, 40).padEnd(40)} confidence ${entry.confidence.toFixed(2)}`
8291
);
8392
}
8493

8594
console.log('─'.repeat(60));
86-
console.log(`Total: ${entries.length} Passed: ${passed} Failed: ${failed}`);
95+
console.log(
96+
`Total: ${entries.length} Passed: ${passed} Failed: ${failed}`
97+
);
8798
} finally {
8899
db.close();
89100
}

scripts/verify-dist.cjs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,21 @@ function main() {
4343
join(dist, 'cli/codex-sm-danger.js'),
4444
join(dist, 'cli/claude-sm.js'),
4545
join(dist, 'cli/claude-sm-danger.js'),
46+
join(dist, 'cli/hermes-sm.js'),
47+
join(dist, 'cli/opencode-sm.js'),
48+
join(dist, 'cli/gemini-sm.js'),
4649
];
4750
requiredFiles.forEach(checkFile);
4851

4952
// 2) Bin launchers reference dist/src/* (ESM dynamic import)
50-
fileContains('bin/codex-sm', "import('../dist/src/cli/codex-sm.js')");
51-
fileContains('bin/codex-smd', "import('../dist/src/cli/codex-sm-danger.js')");
52-
fileContains('bin/claude-sm', "import('../dist/src/cli/claude-sm.js')");
53-
fileContains('bin/claude-smd', "import('../dist/src/cli/claude-sm-danger.js')");
53+
fileContains('bin/codex-sm', "../dist/src/cli/codex-sm.js");
54+
fileContains('bin/codex-smd', "../dist/src/cli/codex-sm-danger.js");
55+
fileContains('bin/claude-sm', "../dist/src/cli/claude-sm.js");
56+
fileContains('bin/claude-smd', "../dist/src/cli/claude-sm-danger.js");
57+
fileContains('bin/hermes-sm', "../dist/src/cli/hermes-sm.js");
58+
fileContains('bin/hermes-smd', "../dist/src/cli/hermes-sm.js");
59+
fileContains('bin/opencode-sm', "../dist/src/cli/opencode-sm.js");
60+
fileContains('bin/gemini-sm', "../dist/src/cli/gemini-sm.js");
5461

5562
// 3) package.json scripts point to correct paths
5663
try {

src/cli/commands/scaffold.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ const TEMPLATES: Record<string, string> = {
2121
'company/design.md':
2222
'---\nname: Design System\ndescription: Logos, colors, components\n---\n\n# Design System\n\n## Colors\n- Primary:\n- Secondary:\n\n## Logos\n- [paths or URLs]\n',
2323
'wiki/README.md':
24-
'# Wiki — SOPs & Playbooks\n\nAdd markdown files here. Files with skill frontmatter become Claude skills.\n',
24+
'# Wiki — SOPs & Playbooks\n\nAdd markdown files here. Files with skill frontmatter become Claude skills.\n\nSOPs should follow the Company OS schema:\n\n- `## Objective`\n- `## Procedure`\n- `## Verification`\n- `## Non-compliance`\n\nEach SOP must reference a PROSE Expectation from `docs/specs/COMPANY-OS-PROSE.md`.\n',
25+
'wiki/SOP-301-onboarding.md':
26+
'# SOP-301 New Hire Onboarding\n\n**Owner:** People Ops \n**Status:** Active \n**Related PROSE Expectation:** [E.1 Onboarding completeness](../docs/specs/COMPANY-OS-PROSE.md#e1-onboarding-completeness)\n\n## Objective\nEnsure every new hire has accounts, hardware, and access documented before their start date.\n\n## Procedure\n\n1. **Pre-start checklist (5 days before)**\n - Hiring manager opens an onboarding ticket.\n - People Ops confirms laptop requirement and shipping address.\n\n2. **Account provisioning (3 days before)**\n - IT creates SSO account and adds the hire to default groups.\n - People Ops sends welcome email with first-week schedule.\n\n3. **Access verification (1 day before)**\n - Hiring manager verifies the hire can log in to primary systems.\n\n## Verification\n\n- Run audit: `stackmemory company-os audit onboarding`\n- Expected result: 100% of hires in last 30 days have completed checklist.\n\n## Non-compliance\n\nOnboarding missing SSO access or hardware assignment on start date is non-compliant.\n',
2527
'skills/README.md':
2628
'# Skills\n\nClaude skill-packs. Each file is a markdown instruction set with frontmatter.\n\n```yaml\n---\nname: skill-name\ndescription: What this skill does\nactivates_on: [keyword1, keyword2]\nversion: "1.0"\n---\n```\n',
2729
'clients/README.md':

src/core/wiki/wiki-compiler.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export class WikiCompiler {
106106
concepts: string;
107107
sources: string;
108108
synthesis: string;
109+
sops: string;
109110
};
110111

111112
constructor(config: WikiConfig) {
@@ -121,6 +122,7 @@ export class WikiCompiler {
121122
concepts: join(this.config.wikiDir, 'concepts'),
122123
sources: join(this.config.wikiDir, 'sources'),
123124
synthesis: join(this.config.wikiDir, 'synthesis'),
125+
sops: join(this.config.wikiDir, 'sops'),
124126
};
125127
}
126128

@@ -208,6 +210,10 @@ export class WikiCompiler {
208210
created.push(path);
209211
}
210212

213+
// 6. Derived Company OS SOPs from anchors
214+
const derivedSops = this.generateDerivedSops(ctx.anchors);
215+
created.push(...derivedSops);
216+
211217
this.updateIndex();
212218
this.appendLog(
213219
`## [${new Date().toISOString().slice(0, 10)}] create | full wiki`,
@@ -312,6 +318,10 @@ export class WikiCompiler {
312318
else created.push(path);
313319
}
314320

321+
// 6. Derived Company OS SOPs from anchors
322+
const derivedSops = this.generateDerivedSops(ctx.anchors);
323+
created.push(...derivedSops);
324+
315325
if (created.length > 0 || updated.length > 0) {
316326
this.updateIndex();
317327
this.appendLog(
@@ -765,6 +775,12 @@ export class WikiCompiler {
765775
f.endsWith('.md')
766776
).length;
767777
}
778+
// Backfill categories that may not exist yet
779+
byCategory['entities'] ??= 0;
780+
byCategory['concepts'] ??= 0;
781+
byCategory['sources'] ??= 0;
782+
byCategory['synthesis'] ??= 0;
783+
byCategory['sops'] ??= 0;
768784

769785
const logPath = join(this.dirs.root, 'log.md');
770786
let lastCompile: string | null = null;
@@ -1152,6 +1168,117 @@ export class WikiCompiler {
11521168
return groups;
11531169
}
11541170

1171+
/**
1172+
* Derive Company OS SOPs from recurring frame anchors.
1173+
* Creates lightweight SOP drafts in wiki/sops/ for DECISION, CONSTRAINT, RISK, FACT anchors.
1174+
*/
1175+
private generateDerivedSops(anchors: AnchorRow[]): string[] {
1176+
const created: string[] = [];
1177+
const derivedConfig: Array<{
1178+
type: string;
1179+
id: number;
1180+
name: string;
1181+
proseId: string;
1182+
objective: string;
1183+
}> = [
1184+
{
1185+
type: 'DECISION',
1186+
id: 401,
1187+
name: 'Decision-derived Process',
1188+
proseId: 'E.9',
1189+
objective:
1190+
'Preserve recurring decisions as repeatable guidance so the team does not re-debate settled questions.',
1191+
},
1192+
{
1193+
type: 'CONSTRAINT',
1194+
id: 402,
1195+
name: 'Constraint-derived Process',
1196+
proseId: 'E.10',
1197+
objective:
1198+
'Preserve recurring constraints as repeatable guidance so the team respects known boundaries.',
1199+
},
1200+
{
1201+
type: 'RISK',
1202+
id: 403,
1203+
name: 'Risk-derived Process',
1204+
proseId: 'E.11',
1205+
objective:
1206+
'Preserve recurring risks as repeatable guidance so the team mitigates them consistently.',
1207+
},
1208+
{
1209+
type: 'FACT',
1210+
id: 404,
1211+
name: 'Fact-derived Process',
1212+
proseId: 'E.12',
1213+
objective:
1214+
'Preserve recurring facts as repeatable guidance so the team operates from shared knowledge.',
1215+
},
1216+
];
1217+
1218+
for (const config of derivedConfig) {
1219+
const typeAnchors = anchors.filter((a) => a.type === config.type);
1220+
if (typeAnchors.length === 0) continue;
1221+
1222+
const path = `sops/SOP-${config.id}-${this.slugify(config.name)}.md`;
1223+
const items = typeAnchors
1224+
.slice(-20)
1225+
.map((a) => {
1226+
const date = new Date(a.created_at * 1000).toISOString().slice(0, 10);
1227+
return `- ${a.text} _(observed ${date} in [[sources/${this.slugify(a.frame_name)}]])_`;
1228+
})
1229+
.join('\n');
1230+
1231+
const article = [
1232+
'---',
1233+
`title: "SOP-${config.id} ${config.name}"`,
1234+
`category: sop`,
1235+
`derived_from: "${config.type} anchors"`,
1236+
`created: ${new Date().toISOString()}`,
1237+
`updated: ${new Date().toISOString()}`,
1238+
`tags: [sop, derived, ${config.type.toLowerCase()}]`,
1239+
'---',
1240+
'',
1241+
`# SOP-${config.id} ${config.name}`,
1242+
'',
1243+
'**Owner:** Derived from frame anchors ',
1244+
'**Status:** Active ',
1245+
`**Related PROSE Expectation:** [${config.proseId} ${config.name}](../../docs/specs/COMPANY-OS-PROSE.md)`,
1246+
'',
1247+
'## Objective',
1248+
config.objective,
1249+
'',
1250+
'## Procedure',
1251+
'',
1252+
`1. **Review**`,
1253+
` - Review the latest ${config.type} anchors below before taking action.`,
1254+
'',
1255+
`2. **Apply**`,
1256+
` - Treat these ${config.type.toLowerCase()}s as guidance for current and future work.`,
1257+
'',
1258+
`3. **Update**`,
1259+
` - When a ${config.type.toLowerCase()} changes, record the new state in a frame so this SOP can be regenerated.`,
1260+
'',
1261+
'## Verification',
1262+
'',
1263+
`- Run \`stackmemory company-os audit ${config.type.toLowerCase()}-derived\``,
1264+
`- Expected result: the derived ${config.type.toLowerCase()}s below are reflected in current work.`,
1265+
'',
1266+
'### Derived anchors',
1267+
`${items}`,
1268+
'',
1269+
'## Non-compliance',
1270+
'',
1271+
`Ignoring or overriding these ${config.type.toLowerCase()}s without a new recorded decision is non-compliant.`,
1272+
'',
1273+
].join('\n');
1274+
1275+
this.writeArticle(path, article);
1276+
created.push(path);
1277+
}
1278+
1279+
return created;
1280+
}
1281+
11551282
// ── Reading helpers ──
11561283

11571284
private readExistingSources(): Array<{ title: string; path: string }> {

0 commit comments

Comments
 (0)