@organon-methodology/tools 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/cli/commands/coverage.d.ts +12 -0
- package/dist/cli/commands/coverage.d.ts.map +1 -0
- package/dist/cli/commands/coverage.js +80 -0
- package/dist/cli/commands/coverage.js.map +1 -0
- package/dist/cli/commands/find.d.ts +15 -0
- package/dist/cli/commands/find.d.ts.map +1 -0
- package/dist/cli/commands/find.js +81 -0
- package/dist/cli/commands/find.js.map +1 -0
- package/dist/cli/commands/generate-tests.d.ts +14 -0
- package/dist/cli/commands/generate-tests.d.ts.map +1 -0
- package/dist/cli/commands/generate-tests.js +87 -0
- package/dist/cli/commands/generate-tests.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +15 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +77 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/health.d.ts +13 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +79 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/init.d.ts +14 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +132 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +11 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +26 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/query.d.ts +19 -0
- package/dist/cli/commands/query.d.ts.map +1 -0
- package/dist/cli/commands/query.js +103 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/upgrade.d.ts +13 -0
- package/dist/cli/commands/upgrade.d.ts.map +1 -0
- package/dist/cli/commands/upgrade.js +162 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +13 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +89 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +12 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +67 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/index.d.ts +19 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +59 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/add-protocols-array.d.ts +17 -0
- package/dist/core/add-protocols-array.d.ts.map +1 -0
- package/dist/core/add-protocols-array.js +95 -0
- package/dist/core/add-protocols-array.js.map +1 -0
- package/dist/core/add-protocols-array.test.d.ts +2 -0
- package/dist/core/add-protocols-array.test.d.ts.map +1 -0
- package/dist/core/add-protocols-array.test.js +86 -0
- package/dist/core/add-protocols-array.test.js.map +1 -0
- package/dist/core/config.d.ts +44 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +130 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/config.test.d.ts +2 -0
- package/dist/core/config.test.d.ts.map +1 -0
- package/dist/core/config.test.js +88 -0
- package/dist/core/config.test.js.map +1 -0
- package/dist/core/find.d.ts +29 -0
- package/dist/core/find.d.ts.map +1 -0
- package/dist/core/find.js +191 -0
- package/dist/core/find.js.map +1 -0
- package/dist/core/find.test.d.ts +2 -0
- package/dist/core/find.test.d.ts.map +1 -0
- package/dist/core/find.test.js +91 -0
- package/dist/core/find.test.js.map +1 -0
- package/dist/core/frontmatter-parser.d.ts +45 -0
- package/dist/core/frontmatter-parser.d.ts.map +1 -0
- package/dist/core/frontmatter-parser.js +122 -0
- package/dist/core/frontmatter-parser.js.map +1 -0
- package/dist/core/frontmatter-parser.test.d.ts +2 -0
- package/dist/core/frontmatter-parser.test.d.ts.map +1 -0
- package/dist/core/frontmatter-parser.test.js +200 -0
- package/dist/core/frontmatter-parser.test.js.map +1 -0
- package/dist/core/generate-frontmatter.d.ts +27 -0
- package/dist/core/generate-frontmatter.d.ts.map +1 -0
- package/dist/core/generate-frontmatter.js +219 -0
- package/dist/core/generate-frontmatter.js.map +1 -0
- package/dist/core/generate-frontmatter.test.d.ts +2 -0
- package/dist/core/generate-frontmatter.test.d.ts.map +1 -0
- package/dist/core/generate-frontmatter.test.js +161 -0
- package/dist/core/generate-frontmatter.test.js.map +1 -0
- package/dist/core/generate-tests.d.ts +41 -0
- package/dist/core/generate-tests.d.ts.map +1 -0
- package/dist/core/generate-tests.js +191 -0
- package/dist/core/generate-tests.js.map +1 -0
- package/dist/core/generate-tests.test.d.ts +2 -0
- package/dist/core/generate-tests.test.d.ts.map +1 -0
- package/dist/core/generate-tests.test.js +205 -0
- package/dist/core/generate-tests.test.js.map +1 -0
- package/dist/core/health.d.ts +16 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +156 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/health.test.d.ts +2 -0
- package/dist/core/health.test.d.ts.map +1 -0
- package/dist/core/health.test.js +97 -0
- package/dist/core/health.test.js.map +1 -0
- package/dist/core/init.d.ts +25 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +100 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/init.test.d.ts +8 -0
- package/dist/core/init.test.d.ts.map +1 -0
- package/dist/core/init.test.js +203 -0
- package/dist/core/init.test.js.map +1 -0
- package/dist/core/invariant-coverage.d.ts +41 -0
- package/dist/core/invariant-coverage.d.ts.map +1 -0
- package/dist/core/invariant-coverage.js +181 -0
- package/dist/core/invariant-coverage.js.map +1 -0
- package/dist/core/invariant-coverage.test.d.ts +2 -0
- package/dist/core/invariant-coverage.test.d.ts.map +1 -0
- package/dist/core/invariant-coverage.test.js +188 -0
- package/dist/core/invariant-coverage.test.js.map +1 -0
- package/dist/core/meta-invariants.test.d.ts +17 -0
- package/dist/core/meta-invariants.test.d.ts.map +1 -0
- package/dist/core/meta-invariants.test.js +191 -0
- package/dist/core/meta-invariants.test.js.map +1 -0
- package/dist/core/node-fs.d.ts +22 -0
- package/dist/core/node-fs.d.ts.map +1 -0
- package/dist/core/node-fs.js +47 -0
- package/dist/core/node-fs.js.map +1 -0
- package/dist/core/organon-invariants.test.d.ts +15 -0
- package/dist/core/organon-invariants.test.d.ts.map +1 -0
- package/dist/core/organon-invariants.test.js +149 -0
- package/dist/core/organon-invariants.test.js.map +1 -0
- package/dist/core/query.d.ts +35 -0
- package/dist/core/query.d.ts.map +1 -0
- package/dist/core/query.js +139 -0
- package/dist/core/query.js.map +1 -0
- package/dist/core/query.test.d.ts +2 -0
- package/dist/core/query.test.d.ts.map +1 -0
- package/dist/core/query.test.js +97 -0
- package/dist/core/query.test.js.map +1 -0
- package/dist/core/suggest-tools.d.ts +15 -0
- package/dist/core/suggest-tools.d.ts.map +1 -0
- package/dist/core/suggest-tools.js +139 -0
- package/dist/core/suggest-tools.js.map +1 -0
- package/dist/core/suggest-tools.test.d.ts +2 -0
- package/dist/core/suggest-tools.test.d.ts.map +1 -0
- package/dist/core/suggest-tools.test.js +122 -0
- package/dist/core/suggest-tools.test.js.map +1 -0
- package/dist/core/test-helpers.d.ts +63 -0
- package/dist/core/test-helpers.d.ts.map +1 -0
- package/dist/core/test-helpers.js +265 -0
- package/dist/core/test-helpers.js.map +1 -0
- package/dist/core/types.d.ts +277 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/upgrade.d.ts +46 -0
- package/dist/core/upgrade.d.ts.map +1 -0
- package/dist/core/upgrade.js +227 -0
- package/dist/core/upgrade.js.map +1 -0
- package/dist/core/upgrade.test.d.ts +8 -0
- package/dist/core/upgrade.test.d.ts.map +1 -0
- package/dist/core/upgrade.test.js +370 -0
- package/dist/core/upgrade.test.js.map +1 -0
- package/dist/core/validate-frontmatter.d.ts +20 -0
- package/dist/core/validate-frontmatter.d.ts.map +1 -0
- package/dist/core/validate-frontmatter.js +529 -0
- package/dist/core/validate-frontmatter.js.map +1 -0
- package/dist/core/validate-frontmatter.test.d.ts +2 -0
- package/dist/core/validate-frontmatter.test.d.ts.map +1 -0
- package/dist/core/validate-frontmatter.test.js +366 -0
- package/dist/core/validate-frontmatter.test.js.map +1 -0
- package/dist/core/validate-workflow.d.ts +17 -0
- package/dist/core/validate-workflow.d.ts.map +1 -0
- package/dist/core/validate-workflow.js +233 -0
- package/dist/core/validate-workflow.js.map +1 -0
- package/dist/core/validate-workflow.test.d.ts +2 -0
- package/dist/core/validate-workflow.test.d.ts.map +1 -0
- package/dist/core/validate-workflow.test.js +205 -0
- package/dist/core/validate-workflow.test.js.map +1 -0
- package/dist/core/verify-tier4-tests.d.ts +34 -0
- package/dist/core/verify-tier4-tests.d.ts.map +1 -0
- package/dist/core/verify-tier4-tests.js +106 -0
- package/dist/core/verify-tier4-tests.js.map +1 -0
- package/dist/core/verify-tier4-tests.test.d.ts +2 -0
- package/dist/core/verify-tier4-tests.test.d.ts.map +1 -0
- package/dist/core/verify-tier4-tests.test.js +140 -0
- package/dist/core/verify-tier4-tests.test.js.map +1 -0
- package/dist/core/verify-triplets.d.ts +17 -0
- package/dist/core/verify-triplets.d.ts.map +1 -0
- package/dist/core/verify-triplets.js +217 -0
- package/dist/core/verify-triplets.js.map +1 -0
- package/dist/core/verify-triplets.test.d.ts +2 -0
- package/dist/core/verify-triplets.test.d.ts.map +1 -0
- package/dist/core/verify-triplets.test.js +147 -0
- package/dist/core/verify-triplets.test.js.map +1 -0
- package/dist/core/verify.d.ts +19 -0
- package/dist/core/verify.d.ts.map +1 -0
- package/dist/core/verify.js +162 -0
- package/dist/core/verify.js.map +1 -0
- package/dist/core/verify.test.d.ts +2 -0
- package/dist/core/verify.test.d.ts.map +1 -0
- package/dist/core/verify.test.js +147 -0
- package/dist/core/verify.test.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/prompts.d.ts +9 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +265 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/resources.d.ts +7 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +111 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +31 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +7 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +130 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/templates/config.d.ts +6 -0
- package/dist/templates/config.d.ts.map +1 -0
- package/dist/templates/config.js +30 -0
- package/dist/templates/config.js.map +1 -0
- package/dist/templates/index.d.ts +7 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +7 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/organon/claude-md.d.ts +7 -0
- package/dist/templates/organon/claude-md.d.ts.map +1 -0
- package/dist/templates/organon/claude-md.js +32 -0
- package/dist/templates/organon/claude-md.js.map +1 -0
- package/dist/templates/organon/ethos.d.ts +5 -0
- package/dist/templates/organon/ethos.d.ts.map +1 -0
- package/dist/templates/organon/ethos.js +69 -0
- package/dist/templates/organon/ethos.js.map +1 -0
- package/dist/templates/organon/index.d.ts +10 -0
- package/dist/templates/organon/index.d.ts.map +1 -0
- package/dist/templates/organon/index.js +10 -0
- package/dist/templates/organon/index.js.map +1 -0
- package/dist/templates/organon/observations-readme.d.ts +5 -0
- package/dist/templates/organon/observations-readme.d.ts.map +1 -0
- package/dist/templates/organon/observations-readme.js +25 -0
- package/dist/templates/organon/observations-readme.js.map +1 -0
- package/dist/templates/organon/philosophy.d.ts +5 -0
- package/dist/templates/organon/philosophy.d.ts.map +1 -0
- package/dist/templates/organon/philosophy.js +75 -0
- package/dist/templates/organon/philosophy.js.map +1 -0
- package/dist/templates/organon/protocols.d.ts +7 -0
- package/dist/templates/organon/protocols.d.ts.map +1 -0
- package/dist/templates/organon/protocols.js +197 -0
- package/dist/templates/organon/protocols.js.map +1 -0
- package/dist/templates/organon/readme.d.ts +5 -0
- package/dist/templates/organon/readme.d.ts.map +1 -0
- package/dist/templates/organon/readme.js +40 -0
- package/dist/templates/organon/readme.js.map +1 -0
- package/dist/templates/skills/domain-feature-design.d.ts +9 -0
- package/dist/templates/skills/domain-feature-design.d.ts.map +1 -0
- package/dist/templates/skills/domain-feature-design.js +123 -0
- package/dist/templates/skills/domain-feature-design.js.map +1 -0
- package/dist/templates/skills/index.d.ts +15 -0
- package/dist/templates/skills/index.d.ts.map +1 -0
- package/dist/templates/skills/index.js +35 -0
- package/dist/templates/skills/index.js.map +1 -0
- package/dist/templates/skills/organon-file-creation.d.ts +9 -0
- package/dist/templates/skills/organon-file-creation.d.ts.map +1 -0
- package/dist/templates/skills/organon-file-creation.js +138 -0
- package/dist/templates/skills/organon-file-creation.js.map +1 -0
- package/dist/templates/skills/quality-review.d.ts +9 -0
- package/dist/templates/skills/quality-review.d.ts.map +1 -0
- package/dist/templates/skills/quality-review.js +127 -0
- package/dist/templates/skills/quality-review.js.map +1 -0
- package/dist/templates/skills/session-compounding.d.ts +9 -0
- package/dist/templates/skills/session-compounding.d.ts.map +1 -0
- package/dist/templates/skills/session-compounding.js +118 -0
- package/dist/templates/skills/session-compounding.js.map +1 -0
- package/dist/templates/skills/verify-and-health.d.ts +9 -0
- package/dist/templates/skills/verify-and-health.d.ts.map +1 -0
- package/dist/templates/skills/verify-and-health.js +99 -0
- package/dist/templates/skills/verify-and-health.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant coverage analysis.
|
|
3
|
+
*
|
|
4
|
+
* Extracts invariant IDs from ETHOS.md frontmatter (type: constraints)
|
|
5
|
+
* and scans test files for @organon-invariant annotations to compute
|
|
6
|
+
* coverage: which invariants have tests, which don't, and which are
|
|
7
|
+
* marked as judgment calls (requiring human review, not automated tests).
|
|
8
|
+
*/
|
|
9
|
+
import type { FileSystem, InvariantCoverageResult, InvariantEntry, OrganonConfig } from './types.js';
|
|
10
|
+
export interface ExtractedInvariant extends InvariantEntry {
|
|
11
|
+
file: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Extract invariant entries from all type: constraints files.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractInvariants(files: Array<{
|
|
17
|
+
path: string;
|
|
18
|
+
frontmatter: {
|
|
19
|
+
type?: string;
|
|
20
|
+
invariants?: InvariantEntry[];
|
|
21
|
+
} | null;
|
|
22
|
+
}>): ExtractedInvariant[];
|
|
23
|
+
/**
|
|
24
|
+
* Scan test files for @organon-invariant annotations.
|
|
25
|
+
* Returns a map: invariant ID → list of "file:line" references.
|
|
26
|
+
*/
|
|
27
|
+
export declare function scanTestAnnotations(fs: FileSystem, projectRoot: string, config: OrganonConfig): Promise<Map<string, string[]>>;
|
|
28
|
+
/**
|
|
29
|
+
* Compute coverage by joining invariants with test annotations.
|
|
30
|
+
*/
|
|
31
|
+
export declare function computeCoverage(invariants: ExtractedInvariant[], annotations: Map<string, string[]>): InvariantCoverageResult;
|
|
32
|
+
/**
|
|
33
|
+
* Full coverage pipeline: discover organon files, extract invariants,
|
|
34
|
+
* scan tests, compute coverage.
|
|
35
|
+
*/
|
|
36
|
+
export declare function computeInvariantCoverage(options: {
|
|
37
|
+
projectRoot: string;
|
|
38
|
+
config: OrganonConfig;
|
|
39
|
+
fs: FileSystem;
|
|
40
|
+
}): Promise<InvariantCoverageResult>;
|
|
41
|
+
//# sourceMappingURL=invariant-coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant-coverage.d.ts","sourceRoot":"","sources":["../../src/core/invariant-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAEV,UAAU,EAEV,uBAAuB,EACvB,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAuBpB,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,IAAI,EAAE,MAAM,CAAC;CACd;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,CAAC,GACnG,kBAAkB,EAAE,CAUtB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAmChC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,kBAAkB,EAAE,EAChC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACjC,uBAAuB,CA6DzB;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,EAAE,EAAE,UAAU,CAAC;CAChB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAgCnC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant coverage analysis.
|
|
3
|
+
*
|
|
4
|
+
* Extracts invariant IDs from ETHOS.md frontmatter (type: constraints)
|
|
5
|
+
* and scans test files for @organon-invariant annotations to compute
|
|
6
|
+
* coverage: which invariants have tests, which don't, and which are
|
|
7
|
+
* marked as judgment calls (requiring human review, not automated tests).
|
|
8
|
+
*/
|
|
9
|
+
import { parseFrontmatter } from './frontmatter-parser.js';
|
|
10
|
+
import { joinPath } from './config.js';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Constants
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
const DEFAULT_TEST_GLOBS = [
|
|
15
|
+
'**/*.test.ts',
|
|
16
|
+
'**/*.test.js',
|
|
17
|
+
'**/*.test.tsx',
|
|
18
|
+
'**/*.test.jsx',
|
|
19
|
+
'**/*.spec.ts',
|
|
20
|
+
'**/*.spec.js',
|
|
21
|
+
'**/test_*.py',
|
|
22
|
+
'**/*_test.go',
|
|
23
|
+
];
|
|
24
|
+
const ANNOTATION_RE = /@organon-invariant\s+(INV-[\w-]+(?:\s+INV-[\w-]+)*)/g;
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Public API
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Extract invariant entries from all type: constraints files.
|
|
30
|
+
*/
|
|
31
|
+
export function extractInvariants(files) {
|
|
32
|
+
const result = [];
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
if (file.frontmatter?.type !== 'constraints')
|
|
35
|
+
continue;
|
|
36
|
+
if (!file.frontmatter.invariants)
|
|
37
|
+
continue;
|
|
38
|
+
for (const inv of file.frontmatter.invariants) {
|
|
39
|
+
result.push({ ...inv, file: file.path });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Scan test files for @organon-invariant annotations.
|
|
46
|
+
* Returns a map: invariant ID → list of "file:line" references.
|
|
47
|
+
*/
|
|
48
|
+
export async function scanTestAnnotations(fs, projectRoot, config) {
|
|
49
|
+
const testGlobs = config.testGlobs ?? DEFAULT_TEST_GLOBS;
|
|
50
|
+
const ignorePatterns = config.testIgnorePatterns ?? config.ignorePatterns;
|
|
51
|
+
const annotations = new Map();
|
|
52
|
+
for (const pattern of testGlobs) {
|
|
53
|
+
const files = await fs.glob(pattern, { cwd: projectRoot, ignore: ignorePatterns });
|
|
54
|
+
for (const file of files) {
|
|
55
|
+
const absPath = joinPath(projectRoot, file);
|
|
56
|
+
let content;
|
|
57
|
+
try {
|
|
58
|
+
content = await fs.readFile(absPath);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const lines = content.split('\n');
|
|
64
|
+
for (let i = 0; i < lines.length; i++) {
|
|
65
|
+
const line = lines[i];
|
|
66
|
+
let match;
|
|
67
|
+
// Reset regex state for each line
|
|
68
|
+
ANNOTATION_RE.lastIndex = 0;
|
|
69
|
+
while ((match = ANNOTATION_RE.exec(line)) !== null) {
|
|
70
|
+
const ids = match[1].split(/\s+/);
|
|
71
|
+
for (const id of ids) {
|
|
72
|
+
const refs = annotations.get(id) ?? [];
|
|
73
|
+
refs.push(`${file}:${i + 1}`);
|
|
74
|
+
annotations.set(id, refs);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return annotations;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Compute coverage by joining invariants with test annotations.
|
|
84
|
+
*/
|
|
85
|
+
export function computeCoverage(invariants, annotations) {
|
|
86
|
+
const errors = [];
|
|
87
|
+
const warnings = [];
|
|
88
|
+
const entries = [];
|
|
89
|
+
let covered = 0;
|
|
90
|
+
let uncovered = 0;
|
|
91
|
+
let judgmentCall = 0;
|
|
92
|
+
for (const inv of invariants) {
|
|
93
|
+
const tests = annotations.get(inv.id) ?? [];
|
|
94
|
+
if (inv.judgment_call) {
|
|
95
|
+
entries.push({
|
|
96
|
+
id: inv.id,
|
|
97
|
+
name: inv.name,
|
|
98
|
+
file: inv.file,
|
|
99
|
+
status: 'judgment_call',
|
|
100
|
+
tests,
|
|
101
|
+
});
|
|
102
|
+
judgmentCall++;
|
|
103
|
+
}
|
|
104
|
+
else if (tests.length > 0) {
|
|
105
|
+
entries.push({
|
|
106
|
+
id: inv.id,
|
|
107
|
+
name: inv.name,
|
|
108
|
+
file: inv.file,
|
|
109
|
+
status: 'covered',
|
|
110
|
+
tests,
|
|
111
|
+
});
|
|
112
|
+
covered++;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
entries.push({
|
|
116
|
+
id: inv.id,
|
|
117
|
+
name: inv.name,
|
|
118
|
+
file: inv.file,
|
|
119
|
+
status: 'uncovered',
|
|
120
|
+
tests: [],
|
|
121
|
+
});
|
|
122
|
+
uncovered++;
|
|
123
|
+
errors.push({
|
|
124
|
+
severity: 'error',
|
|
125
|
+
code: 'INVARIANT_UNCOVERED',
|
|
126
|
+
message: `Invariant ${inv.id} (${inv.name}) has no test with @organon-invariant annotation`,
|
|
127
|
+
file: inv.file,
|
|
128
|
+
suggestion: `Add a test with // @organon-invariant ${inv.id} comment`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
success: uncovered === 0,
|
|
134
|
+
invariants: entries,
|
|
135
|
+
summary: {
|
|
136
|
+
total: invariants.length,
|
|
137
|
+
covered,
|
|
138
|
+
uncovered,
|
|
139
|
+
judgment_call: judgmentCall,
|
|
140
|
+
},
|
|
141
|
+
errors,
|
|
142
|
+
warnings,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Full coverage pipeline: discover organon files, extract invariants,
|
|
147
|
+
* scan tests, compute coverage.
|
|
148
|
+
*/
|
|
149
|
+
export async function computeInvariantCoverage(options) {
|
|
150
|
+
const { projectRoot, config, fs: fileSystem } = options;
|
|
151
|
+
// Discover organon files and parse frontmatter (deduplicate by path)
|
|
152
|
+
const seenPaths = new Set();
|
|
153
|
+
const parsedFiles = [];
|
|
154
|
+
for (const organonPath of config.organonPaths) {
|
|
155
|
+
for (const pattern of config.organonGlobs) {
|
|
156
|
+
const fullPattern = organonPath === '.' ? pattern : `${organonPath}/${pattern}`;
|
|
157
|
+
const matches = await fileSystem.glob(fullPattern, {
|
|
158
|
+
cwd: projectRoot,
|
|
159
|
+
ignore: config.ignorePatterns,
|
|
160
|
+
});
|
|
161
|
+
for (const file of matches) {
|
|
162
|
+
if (seenPaths.has(file))
|
|
163
|
+
continue;
|
|
164
|
+
seenPaths.add(file);
|
|
165
|
+
const absPath = joinPath(projectRoot, file);
|
|
166
|
+
try {
|
|
167
|
+
const content = await fileSystem.readFile(absPath);
|
|
168
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
169
|
+
parsedFiles.push({ path: file, frontmatter });
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Skip unreadable files
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const invariants = extractInvariants(parsedFiles);
|
|
178
|
+
const annotations = await scanTestAnnotations(fileSystem, projectRoot, config);
|
|
179
|
+
return computeCoverage(invariants, annotations);
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=invariant-coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant-coverage.js","sourceRoot":"","sources":["../../src/core/invariant-coverage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant-coverage.test.d.ts","sourceRoot":"","sources":["../../src/core/invariant-coverage.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { extractInvariants, scanTestAnnotations, computeCoverage, computeInvariantCoverage } from './invariant-coverage.js';
|
|
3
|
+
import { MemoryFileSystem, makeEthos } from './test-helpers.js';
|
|
4
|
+
function makeConfig(projectRoot) {
|
|
5
|
+
return {
|
|
6
|
+
projectRoot,
|
|
7
|
+
organonPaths: ['.'],
|
|
8
|
+
organonGlobs: ['**/ETHOS.md'],
|
|
9
|
+
ignorePatterns: ['node_modules/**'],
|
|
10
|
+
workflowPaths: {},
|
|
11
|
+
freshnessThresholdHours: 24,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
describe('extractInvariants', () => {
|
|
15
|
+
it('extracts invariants from constraints files', () => {
|
|
16
|
+
const files = [
|
|
17
|
+
{
|
|
18
|
+
path: 'ETHOS.md',
|
|
19
|
+
frontmatter: {
|
|
20
|
+
type: 'constraints',
|
|
21
|
+
invariants: [
|
|
22
|
+
{ id: 'INV-META-1', name: 'ethos-required' },
|
|
23
|
+
{ id: 'INV-META-2', name: 'identity-first', judgment_call: true },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
const result = extractInvariants(files);
|
|
29
|
+
expect(result).toHaveLength(2);
|
|
30
|
+
expect(result[0]).toEqual({ id: 'INV-META-1', name: 'ethos-required', file: 'ETHOS.md' });
|
|
31
|
+
expect(result[1]).toEqual({ id: 'INV-META-2', name: 'identity-first', judgment_call: true, file: 'ETHOS.md' });
|
|
32
|
+
});
|
|
33
|
+
it('ignores non-constraints files', () => {
|
|
34
|
+
const files = [
|
|
35
|
+
{
|
|
36
|
+
path: 'PHILOSOPHY.md',
|
|
37
|
+
frontmatter: {
|
|
38
|
+
type: 'rationale',
|
|
39
|
+
invariants: [{ id: 'INV-X-1', name: 'should-not-appear' }],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
const result = extractInvariants(files);
|
|
44
|
+
expect(result).toHaveLength(0);
|
|
45
|
+
});
|
|
46
|
+
it('handles files with no invariants array', () => {
|
|
47
|
+
const files = [
|
|
48
|
+
{
|
|
49
|
+
path: 'ETHOS.md',
|
|
50
|
+
frontmatter: { type: 'constraints' },
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
const result = extractInvariants(files);
|
|
54
|
+
expect(result).toHaveLength(0);
|
|
55
|
+
});
|
|
56
|
+
it('handles files with null frontmatter', () => {
|
|
57
|
+
const files = [{ path: 'ETHOS.md', frontmatter: null }];
|
|
58
|
+
const result = extractInvariants(files);
|
|
59
|
+
expect(result).toHaveLength(0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('scanTestAnnotations', () => {
|
|
63
|
+
it('finds annotations in test files', async () => {
|
|
64
|
+
const fs = new MemoryFileSystem({
|
|
65
|
+
'/project/src/core/verify.test.ts': `import { describe, it } from 'vitest';
|
|
66
|
+
// @organon-invariant INV-META-1
|
|
67
|
+
it('ethos is required', () => {
|
|
68
|
+
expect(true).toBe(true);
|
|
69
|
+
});`,
|
|
70
|
+
});
|
|
71
|
+
const config = makeConfig('/project');
|
|
72
|
+
const result = await scanTestAnnotations(fs, '/project', config);
|
|
73
|
+
expect(result.get('INV-META-1')).toEqual(['src/core/verify.test.ts:2']);
|
|
74
|
+
});
|
|
75
|
+
it('handles multiple IDs on one line', async () => {
|
|
76
|
+
const fs = new MemoryFileSystem({
|
|
77
|
+
'/project/src/test.test.ts': `// @organon-invariant INV-META-1 INV-META-2
|
|
78
|
+
it('tests both', () => {});`,
|
|
79
|
+
});
|
|
80
|
+
const config = makeConfig('/project');
|
|
81
|
+
const result = await scanTestAnnotations(fs, '/project', config);
|
|
82
|
+
expect(result.get('INV-META-1')).toEqual(['src/test.test.ts:1']);
|
|
83
|
+
expect(result.get('INV-META-2')).toEqual(['src/test.test.ts:1']);
|
|
84
|
+
});
|
|
85
|
+
it('finds annotations across multiple files', async () => {
|
|
86
|
+
const fs = new MemoryFileSystem({
|
|
87
|
+
'/project/src/a.test.ts': `// @organon-invariant INV-META-1
|
|
88
|
+
it('test a', () => {});`,
|
|
89
|
+
'/project/src/b.test.ts': `// @organon-invariant INV-META-1
|
|
90
|
+
it('test b', () => {});`,
|
|
91
|
+
});
|
|
92
|
+
const config = makeConfig('/project');
|
|
93
|
+
const result = await scanTestAnnotations(fs, '/project', config);
|
|
94
|
+
expect(result.get('INV-META-1')).toHaveLength(2);
|
|
95
|
+
});
|
|
96
|
+
it('returns empty map when no test files exist', async () => {
|
|
97
|
+
const fs = new MemoryFileSystem({});
|
|
98
|
+
const config = makeConfig('/project');
|
|
99
|
+
const result = await scanTestAnnotations(fs, '/project', config);
|
|
100
|
+
expect(result.size).toBe(0);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('computeCoverage', () => {
|
|
104
|
+
it('classifies covered invariants', () => {
|
|
105
|
+
const invariants = [
|
|
106
|
+
{ id: 'INV-META-1', name: 'ethos-required', file: 'ETHOS.md' },
|
|
107
|
+
];
|
|
108
|
+
const annotations = new Map([['INV-META-1', ['test.ts:5']]]);
|
|
109
|
+
const result = computeCoverage(invariants, annotations);
|
|
110
|
+
expect(result.success).toBe(true);
|
|
111
|
+
expect(result.invariants[0].status).toBe('covered');
|
|
112
|
+
expect(result.summary.covered).toBe(1);
|
|
113
|
+
expect(result.summary.uncovered).toBe(0);
|
|
114
|
+
});
|
|
115
|
+
it('classifies uncovered invariants', () => {
|
|
116
|
+
const invariants = [
|
|
117
|
+
{ id: 'INV-META-1', name: 'ethos-required', file: 'ETHOS.md' },
|
|
118
|
+
];
|
|
119
|
+
const annotations = new Map();
|
|
120
|
+
const result = computeCoverage(invariants, annotations);
|
|
121
|
+
expect(result.success).toBe(false);
|
|
122
|
+
expect(result.invariants[0].status).toBe('uncovered');
|
|
123
|
+
expect(result.summary.uncovered).toBe(1);
|
|
124
|
+
expect(result.errors).toContainEqual(expect.objectContaining({ code: 'INVARIANT_UNCOVERED' }));
|
|
125
|
+
});
|
|
126
|
+
it('classifies judgment_call invariants', () => {
|
|
127
|
+
const invariants = [
|
|
128
|
+
{ id: 'INV-META-2', name: 'identity-first', judgment_call: true, file: 'ETHOS.md' },
|
|
129
|
+
];
|
|
130
|
+
const annotations = new Map();
|
|
131
|
+
const result = computeCoverage(invariants, annotations);
|
|
132
|
+
expect(result.success).toBe(true);
|
|
133
|
+
expect(result.invariants[0].status).toBe('judgment_call');
|
|
134
|
+
expect(result.summary.judgment_call).toBe(1);
|
|
135
|
+
expect(result.summary.uncovered).toBe(0);
|
|
136
|
+
});
|
|
137
|
+
it('reports success when all non-judgment invariants are covered', () => {
|
|
138
|
+
const invariants = [
|
|
139
|
+
{ id: 'INV-META-1', name: 'ethos-required', file: 'ETHOS.md' },
|
|
140
|
+
{ id: 'INV-META-2', name: 'identity-first', judgment_call: true, file: 'ETHOS.md' },
|
|
141
|
+
{ id: 'INV-META-3', name: 'principles-prioritized', file: 'ETHOS.md' },
|
|
142
|
+
];
|
|
143
|
+
const annotations = new Map([
|
|
144
|
+
['INV-META-1', ['a.test.ts:1']],
|
|
145
|
+
['INV-META-3', ['b.test.ts:2']],
|
|
146
|
+
]);
|
|
147
|
+
const result = computeCoverage(invariants, annotations);
|
|
148
|
+
expect(result.success).toBe(true);
|
|
149
|
+
expect(result.summary).toEqual({ total: 3, covered: 2, uncovered: 0, judgment_call: 1 });
|
|
150
|
+
});
|
|
151
|
+
it('reports failure when any non-judgment invariant is uncovered', () => {
|
|
152
|
+
const invariants = [
|
|
153
|
+
{ id: 'INV-META-1', name: 'ethos-required', file: 'ETHOS.md' },
|
|
154
|
+
{ id: 'INV-META-2', name: 'identity-first', judgment_call: true, file: 'ETHOS.md' },
|
|
155
|
+
{ id: 'INV-META-3', name: 'uncovered-one', file: 'ETHOS.md' },
|
|
156
|
+
];
|
|
157
|
+
const annotations = new Map([
|
|
158
|
+
['INV-META-1', ['a.test.ts:1']],
|
|
159
|
+
]);
|
|
160
|
+
const result = computeCoverage(invariants, annotations);
|
|
161
|
+
expect(result.success).toBe(false);
|
|
162
|
+
expect(result.summary.uncovered).toBe(1);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('computeInvariantCoverage (integration)', () => {
|
|
166
|
+
it('runs the full pipeline', async () => {
|
|
167
|
+
const ethosContent = makeEthos({
|
|
168
|
+
name: 'test',
|
|
169
|
+
scope: 'meta',
|
|
170
|
+
extra: {
|
|
171
|
+
invariants: `\n - id: INV-TEST-1\n name: rule-one\n - id: INV-TEST-2\n name: rule-two\n judgment_call: true`,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
const fs = new MemoryFileSystem({
|
|
175
|
+
'/project/ETHOS.md': ethosContent,
|
|
176
|
+
'/project/src/core/check.test.ts': `// @organon-invariant INV-TEST-1
|
|
177
|
+
it('tests rule one', () => {});`,
|
|
178
|
+
});
|
|
179
|
+
const config = makeConfig('/project');
|
|
180
|
+
const result = await computeInvariantCoverage({ projectRoot: '/project', config, fs });
|
|
181
|
+
expect(result.success).toBe(true);
|
|
182
|
+
expect(result.summary.total).toBe(2);
|
|
183
|
+
expect(result.summary.covered).toBe(1);
|
|
184
|
+
expect(result.summary.judgment_call).toBe(1);
|
|
185
|
+
expect(result.summary.uncovered).toBe(0);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
//# sourceMappingURL=invariant-coverage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant-coverage.test.js","sourceRoot":"","sources":["../../src/core/invariant-coverage.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural invariant tests for META-organon and project-level (ORG) invariants.
|
|
3
|
+
*
|
|
4
|
+
* These scan the actual repository to verify structural constraints hold.
|
|
5
|
+
*
|
|
6
|
+
* @organon-invariant INV-META-3 principles-prioritized
|
|
7
|
+
* @organon-invariant INV-META-6 every-file-has-frontmatter
|
|
8
|
+
* @organon-invariant INV-META-7 standardized-section-headings
|
|
9
|
+
* @organon-invariant INV-META-9 enforcement-loop-closable
|
|
10
|
+
* @organon-invariant INV-ORG-1 dogfood-methodology
|
|
11
|
+
* @organon-invariant INV-ORG-2 code-is-source-of-truth
|
|
12
|
+
* @organon-invariant INV-ORG-3 every-file-has-frontmatter
|
|
13
|
+
* @organon-invariant INV-ORG-4 three-artifact-separation
|
|
14
|
+
* @organon-invariant INV-ORG-6 bidirectional-references
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=meta-invariants.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-invariants.test.d.ts","sourceRoot":"","sources":["../../src/core/meta-invariants.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural invariant tests for META-organon and project-level (ORG) invariants.
|
|
3
|
+
*
|
|
4
|
+
* These scan the actual repository to verify structural constraints hold.
|
|
5
|
+
*
|
|
6
|
+
* @organon-invariant INV-META-3 principles-prioritized
|
|
7
|
+
* @organon-invariant INV-META-6 every-file-has-frontmatter
|
|
8
|
+
* @organon-invariant INV-META-7 standardized-section-headings
|
|
9
|
+
* @organon-invariant INV-META-9 enforcement-loop-closable
|
|
10
|
+
* @organon-invariant INV-ORG-1 dogfood-methodology
|
|
11
|
+
* @organon-invariant INV-ORG-2 code-is-source-of-truth
|
|
12
|
+
* @organon-invariant INV-ORG-3 every-file-has-frontmatter
|
|
13
|
+
* @organon-invariant INV-ORG-4 three-artifact-separation
|
|
14
|
+
* @organon-invariant INV-ORG-6 bidirectional-references
|
|
15
|
+
*/
|
|
16
|
+
import { describe, expect } from 'vitest';
|
|
17
|
+
import { testInvariant } from '@organon-methodology/testing/vitest';
|
|
18
|
+
import { resolve } from 'node:path';
|
|
19
|
+
import { readFile, readdir, access } from 'node:fs/promises';
|
|
20
|
+
import { parseFrontmatter, extractSection } from './frontmatter-parser.js';
|
|
21
|
+
const repoRoot = resolve(import.meta.dirname, '../../../..');
|
|
22
|
+
async function readOrganonFile(relPath) {
|
|
23
|
+
return readFile(resolve(repoRoot, relPath), 'utf-8');
|
|
24
|
+
}
|
|
25
|
+
async function exists(relPath) {
|
|
26
|
+
try {
|
|
27
|
+
await access(resolve(repoRoot, relPath));
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// INV-META-3: principles-prioritized
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
describe('INV-META-3: principles-prioritized', () => {
|
|
38
|
+
testInvariant('INV-META-3', 'ETHOS.md files have numbered principles', async () => {
|
|
39
|
+
const ethosFiles = [
|
|
40
|
+
'book-llms/ETHOS.md',
|
|
41
|
+
'organon/ETHOS.md',
|
|
42
|
+
'organon/domains/tools/ETHOS.md',
|
|
43
|
+
'organon/domains/testing/ETHOS.md',
|
|
44
|
+
];
|
|
45
|
+
for (const file of ethosFiles) {
|
|
46
|
+
const content = await readOrganonFile(file);
|
|
47
|
+
const { body } = parseFrontmatter(content);
|
|
48
|
+
const principles = extractSection(body, 'Principles (Prioritized)')
|
|
49
|
+
?? extractSection(body, 'Principles');
|
|
50
|
+
expect(principles, `${file} must have a ## Principles section`).toBeTruthy();
|
|
51
|
+
// Principles must have numbered entries (1., 2., etc.)
|
|
52
|
+
const numbered = principles.match(/^\d+\.\s/gm);
|
|
53
|
+
expect(numbered, `${file} principles must be numbered`).toBeTruthy();
|
|
54
|
+
expect(numbered.length).toBeGreaterThanOrEqual(1);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// INV-META-6 / INV-ORG-3: every-file-has-frontmatter
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
describe('INV-META-6 / INV-ORG-3: every-file-has-frontmatter', () => {
|
|
62
|
+
testInvariant('INV-META-6', 'all organon files have YAML frontmatter', async () => {
|
|
63
|
+
const organonFiles = [
|
|
64
|
+
'book-llms/ETHOS.md',
|
|
65
|
+
'book-llms/PHILOSOPHY.md',
|
|
66
|
+
'organon/ETHOS.md',
|
|
67
|
+
'organon/README.md',
|
|
68
|
+
'organon/domains/tools/ETHOS.md',
|
|
69
|
+
'organon/domains/tools/PHILOSOPHY.md',
|
|
70
|
+
'organon/domains/testing/ETHOS.md',
|
|
71
|
+
'organon/domains/testing/PHILOSOPHY.md',
|
|
72
|
+
'organon/protocols/PROTOCOLS.md',
|
|
73
|
+
];
|
|
74
|
+
for (const file of organonFiles) {
|
|
75
|
+
const content = await readOrganonFile(file);
|
|
76
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
77
|
+
expect(frontmatter, `${file} must have YAML frontmatter`).toBeTruthy();
|
|
78
|
+
expect(frontmatter.type, `${file} frontmatter must have type`).toBeTruthy();
|
|
79
|
+
expect(frontmatter.scope, `${file} frontmatter must have scope`).toBeTruthy();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// INV-META-7: standardized-section-headings
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
describe('INV-META-7: standardized-section-headings', () => {
|
|
87
|
+
testInvariant('INV-META-7', 'ETHOS.md files have required sections', async () => {
|
|
88
|
+
const ethosFiles = [
|
|
89
|
+
'organon/ETHOS.md',
|
|
90
|
+
'organon/domains/tools/ETHOS.md',
|
|
91
|
+
'organon/domains/testing/ETHOS.md',
|
|
92
|
+
];
|
|
93
|
+
const requiredSections = ['Identity', 'Invariants'];
|
|
94
|
+
for (const file of ethosFiles) {
|
|
95
|
+
const content = await readOrganonFile(file);
|
|
96
|
+
const { body } = parseFrontmatter(content);
|
|
97
|
+
for (const section of requiredSections) {
|
|
98
|
+
const found = extractSection(body, section);
|
|
99
|
+
expect(found, `${file} must have ## ${section} section`).toBeTruthy();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// INV-META-9: enforcement-loop-closable
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
describe('INV-META-9: enforcement-loop-closable', () => {
|
|
108
|
+
testInvariant('INV-META-9', 'automated protocols have workflow bindings', async () => {
|
|
109
|
+
const content = await readOrganonFile('organon/protocols/PROTOCOLS.md');
|
|
110
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
111
|
+
const protocols = frontmatter?.protocols;
|
|
112
|
+
expect(protocols, 'PROTOCOLS.md must have protocols array').toBeTruthy();
|
|
113
|
+
for (const proto of protocols) {
|
|
114
|
+
if (proto.automation_tier === 'automated') {
|
|
115
|
+
expect(proto.workflow, `Protocol ${proto.id} is automated but has no workflow binding`).toBeTruthy();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// INV-ORG-1: dogfood-methodology
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
describe('INV-ORG-1: dogfood-methodology', () => {
|
|
124
|
+
testInvariant('INV-ORG-1', 'organon/ directory exists with ETHOS.md', async () => {
|
|
125
|
+
expect(await exists('organon/ETHOS.md')).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
testInvariant('INV-ORG-1', 'organon/protocols/PROTOCOLS.md exists', async () => {
|
|
128
|
+
expect(await exists('organon/protocols/PROTOCOLS.md')).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// INV-ORG-2: code-is-source-of-truth
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
describe('INV-ORG-2: code-is-source-of-truth', () => {
|
|
135
|
+
testInvariant('INV-ORG-2', 'packages/tools/src/ exists with implementation code', async () => {
|
|
136
|
+
expect(await exists('packages/tools/src/core')).toBe(true);
|
|
137
|
+
const entries = await readdir(resolve(repoRoot, 'packages/tools/src/core'));
|
|
138
|
+
const tsFiles = entries.filter((f) => f.endsWith('.ts') && !f.endsWith('.test.ts'));
|
|
139
|
+
expect(tsFiles.length).toBeGreaterThanOrEqual(5);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// INV-ORG-4: three-artifact-separation
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
describe('INV-ORG-4: three-artifact-separation', () => {
|
|
146
|
+
testInvariant('INV-ORG-4', 'ETHOS.md files have type: constraints', async () => {
|
|
147
|
+
const ethosFiles = [
|
|
148
|
+
'organon/ETHOS.md',
|
|
149
|
+
'organon/domains/tools/ETHOS.md',
|
|
150
|
+
'organon/domains/testing/ETHOS.md',
|
|
151
|
+
];
|
|
152
|
+
for (const file of ethosFiles) {
|
|
153
|
+
const content = await readOrganonFile(file);
|
|
154
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
155
|
+
expect(frontmatter?.type, `${file} must be type: constraints`).toBe('constraints');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
testInvariant('INV-ORG-4', 'PHILOSOPHY.md files have type: rationale', async () => {
|
|
159
|
+
const philosophyFiles = [
|
|
160
|
+
'organon/domains/tools/PHILOSOPHY.md',
|
|
161
|
+
'organon/domains/testing/PHILOSOPHY.md',
|
|
162
|
+
];
|
|
163
|
+
for (const file of philosophyFiles) {
|
|
164
|
+
const content = await readOrganonFile(file);
|
|
165
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
166
|
+
expect(frontmatter?.type, `${file} must be type: rationale`).toBe('rationale');
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
testInvariant('INV-ORG-4', 'PROTOCOLS.md files have type: procedures', async () => {
|
|
170
|
+
const content = await readOrganonFile('organon/protocols/PROTOCOLS.md');
|
|
171
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
172
|
+
expect(frontmatter?.type).toBe('procedures');
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// INV-ORG-6: bidirectional-references
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
describe('INV-ORG-6: bidirectional-references', () => {
|
|
179
|
+
testInvariant('INV-ORG-6', 'automated protocols reference workflows that exist as skills', async () => {
|
|
180
|
+
const content = await readOrganonFile('organon/protocols/PROTOCOLS.md');
|
|
181
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
182
|
+
const protocols = frontmatter?.protocols;
|
|
183
|
+
for (const proto of protocols ?? []) {
|
|
184
|
+
if (proto.automation_tier === 'automated' && proto.workflow) {
|
|
185
|
+
const skillPath = `.claude/skills/${proto.workflow}/SKILL.md`;
|
|
186
|
+
expect(await exists(skillPath), `Workflow "${proto.workflow}" for protocol ${proto.id} must exist at ${skillPath}`).toBe(true);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=meta-invariants.test.js.map
|