@intentset/cli 0.10.0 → 0.13.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.
Files changed (53) hide show
  1. package/dist/agents.d.ts.map +1 -1
  2. package/dist/agents.js +43 -2
  3. package/dist/agents.js.map +1 -1
  4. package/dist/commands/context.d.ts.map +1 -1
  5. package/dist/commands/context.js +20 -4
  6. package/dist/commands/context.js.map +1 -1
  7. package/dist/commands/graph.js +1 -1
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +10 -8
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/list.d.ts +49 -0
  12. package/dist/commands/list.d.ts.map +1 -0
  13. package/dist/commands/list.js +295 -0
  14. package/dist/commands/list.js.map +1 -0
  15. package/dist/commands/review.js +1 -1
  16. package/dist/commands/review.js.map +1 -1
  17. package/dist/commands/shared.d.ts +8 -0
  18. package/dist/commands/shared.d.ts.map +1 -1
  19. package/dist/commands/shared.js +15 -5
  20. package/dist/commands/shared.js.map +1 -1
  21. package/dist/glob.d.ts +4 -13
  22. package/dist/glob.d.ts.map +1 -1
  23. package/dist/glob.js +4 -57
  24. package/dist/glob.js.map +1 -1
  25. package/dist/main.d.ts +1 -1
  26. package/dist/main.d.ts.map +1 -1
  27. package/dist/main.js +78 -5
  28. package/dist/main.js.map +1 -1
  29. package/dist/session.d.ts +1 -2
  30. package/dist/session.d.ts.map +1 -1
  31. package/dist/session.js +6 -4
  32. package/dist/session.js.map +1 -1
  33. package/dist/subject.d.ts +2 -0
  34. package/dist/subject.d.ts.map +1 -1
  35. package/dist/subject.js +11 -1
  36. package/dist/subject.js.map +1 -1
  37. package/package.json +9 -9
  38. package/src/agents.ts +43 -2
  39. package/src/commands/context.ts +24 -3
  40. package/src/commands/graph.ts +1 -1
  41. package/src/commands/init.ts +10 -8
  42. package/src/commands/list.ts +346 -0
  43. package/src/commands/review.ts +1 -1
  44. package/src/commands/shared.ts +20 -0
  45. package/src/glob.ts +4 -56
  46. package/src/main.ts +85 -6
  47. package/src/session.ts +5 -4
  48. package/src/subject.ts +13 -1
  49. package/dist/slices.d.ts +0 -24
  50. package/dist/slices.d.ts.map +0 -1
  51. package/dist/slices.js +0 -48
  52. package/dist/slices.js.map +0 -1
  53. package/src/slices.ts +0 -54
@@ -3,10 +3,30 @@
3
3
  * the graph validated, and the CORE003-style refusal for an ID the graph does
4
4
  * not hold.
5
5
  */
6
+ import type { TypeScriptMissingError } from "@intentset/architecture";
6
7
  import { type Diagnostic, ID_PATTERN } from "@intentset/core";
7
8
  import { count } from "../output.ts";
8
9
  import type { Session } from "../session.ts";
9
10
 
11
+ /**
12
+ * What the CLI says when a check needed TypeScript 7's scanner and the `typescript` the repository gave
13
+ * @intentset/architecture is older or absent: the version found and the command that runs the toolchain with its
14
+ * own TypeScript, for the arguments that were given, with pnpm (which resolves the peer to the repository's own
15
+ * copy) and with npm (which installs the peer beside the CLI, so it rarely gets here).
16
+ */
17
+ export function typescriptMissing(error: TypeScriptMissingError, argv: readonly string[]): string {
18
+ const args = argv.length === 0 ? "" : ` ${argv.join(" ")}`;
19
+ const found =
20
+ error.found === null
21
+ ? "no `typescript` resolves from @intentset/architecture"
22
+ : `the \`typescript\` this repository gives @intentset/architecture is ${error.found}`;
23
+ return (
24
+ `intentset: the architecture check needs TypeScript 7, and ${found}. Run the toolchain without installing it, ` +
25
+ `as \`pnpm dlx --package=@intentset/cli --package=typescript@7 intentset${args}\`, or with npm as ` +
26
+ `\`npx -p @intentset/cli -p typescript@7 intentset${args}\`; or give the repository TypeScript 7.`
27
+ );
28
+ }
29
+
10
30
  /** The validation state a query ran over, so a reader knows whether the graph was whole. */
11
31
  export function validationSummary(session: Session, level: string) {
12
32
  return {
package/src/glob.ts CHANGED
@@ -1,58 +1,6 @@
1
1
  /**
2
- * The pattern language of `.intentset/config.yaml` scope and ignore lists,
3
- * which is VSA §3's: repository-relative POSIX paths made of literal segments,
4
- * `*` within one segment, and `**` across zero or more segments. Nothing else
5
- * is special: no `?`, no classes, no braces, no negation, so there is no
6
- * precedence to explain. A pattern and a path match segment by segment, and
7
- * `*` never crosses a `/`.
8
- *
9
- * The architecture package has its own copy for claims. Duplicating thirty
10
- * lines keeps the CLI's core commands independent of it.
2
+ * The pattern language of `.intentset/config.yaml` scope and ignore lists and of slice claims (VSA §3). It moved to
3
+ * `@intentset/core` in 0.11.0 with the slice helpers that use it (ADR 0016), and the CLI re-exports it, so
4
+ * `matchGlob` and `matchAny` stay on its public surface.
11
5
  */
12
-
13
- /** VSA §3: true when the repository-relative POSIX `path` matches `pattern`. */
14
- export function matchGlob(pattern: string, path: string): boolean {
15
- const p = normalize(pattern).split("/");
16
- const s = path.split("/");
17
- // Memo of (pattern index, path index) pairs already known not to match, so
18
- // several `**` in one pattern stay polynomial rather than exponential.
19
- const failed = new Set<number>();
20
- const width = s.length + 1;
21
- const match = (i: number, j: number): boolean => {
22
- if (failed.has(i * width + j)) return false;
23
- let ok: boolean;
24
- if (i === p.length) ok = j === s.length;
25
- else if (p[i] === "**") ok = match(i + 1, j) || (j < s.length && match(i, j + 1));
26
- else ok = j < s.length && matchSegment(p[i], s[j]) && match(i + 1, j + 1);
27
- if (!ok) failed.add(i * width + j);
28
- return ok;
29
- };
30
- return match(0, 0);
31
- }
32
-
33
- /** True when any of `patterns` matches `path`. */
34
- export function matchAny(patterns: readonly string[], path: string): boolean {
35
- return patterns.some((pattern) => matchGlob(pattern, path));
36
- }
37
-
38
- /** A leading `./` names the same path; repeated `/` separate nothing. */
39
- function normalize(pattern: string): string {
40
- return pattern.replace(/^(?:\.\/)+/, "").replace(/\/{2,}/g, "/");
41
- }
42
-
43
- const SEGMENTS = new Map<string, RegExp>();
44
-
45
- /** One segment: literal text, with each `*` standing for any run of characters other than `/`. */
46
- function matchSegment(pattern: string, name: string): boolean {
47
- if (!pattern.includes("*")) return pattern === name;
48
- let regex = SEGMENTS.get(pattern);
49
- if (regex === undefined) {
50
- const body = pattern
51
- .split("*")
52
- .map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&"))
53
- .join("[^/]*");
54
- regex = new RegExp(`^${body}$`);
55
- SEGMENTS.set(pattern, regex);
56
- }
57
- return regex.test(name);
58
- }
6
+ export { matchAny, matchGlob } from "@intentset/core";
package/src/main.ts CHANGED
@@ -4,14 +4,15 @@
4
4
  * exiting, so tests run it in-process with captured io.
5
5
  *
6
6
  * Exit codes (Core §11, ADR 0004): 0 no errors, 1 validation errors, 2
7
- * invocation or tool failure. validate, graph, architecture check, impact,
8
- * context, review, evidence stale, serve, mcp and guide never write; graph --out, architecture
7
+ * invocation or tool failure (list exits 0 whenever it answered, and 1 only
8
+ * with --check). validate, graph, architecture check, impact, context, list,
9
+ * review, evidence stale, serve, mcp and guide never write; graph --out, architecture
9
10
  * check --write-baseline, evidence import --out, publish --out and serve
10
11
  * --out write only what they name; init only creates; migrate rewrites the
11
12
  * two version values of each record's frontmatter and nothing else.
12
13
  */
13
14
  import { parseArgs } from "node:util";
14
- import type { Mode } from "@intentset/architecture";
15
+ import { type Mode, TypeScriptMissingError } from "@intentset/architecture";
15
16
  import { EVIDENCE_BINDINGS, type EvidenceBinding, LEVELS, type Level } from "@intentset/core";
16
17
  import type { RunScope } from "@intentset/verification";
17
18
  import { architectureCommand } from "./commands/architecture.ts";
@@ -22,6 +23,13 @@ import { graphCommand, graphUsageProblem, parseRelease } from "./commands/graph.
22
23
  import { guideCommand } from "./commands/guide.ts";
23
24
  import { impactCommand } from "./commands/impact.ts";
24
25
  import { initCommand } from "./commands/init.ts";
26
+ import {
27
+ listCommand,
28
+ listFromExportCommand,
29
+ type ListOptions,
30
+ listUsageProblem,
31
+ SELECTOR_OPTIONS,
32
+ } from "./commands/list.ts";
25
33
  import { migrateCommand, migrateUsageProblem } from "./commands/migrate.ts";
26
34
  import { mcpCommand, mcpUsageProblem } from "./commands/mcp.ts";
27
35
  import { publishCommand, publishUsageProblem } from "./commands/publish.ts";
@@ -29,6 +37,7 @@ import { reviewCommand } from "./commands/review.ts";
29
37
  import { serveCommand } from "./commands/serve.ts";
30
38
  import { validateCommand } from "./commands/validate.ts";
31
39
  import { type Io, TOOL } from "./output.ts";
40
+ import { typescriptMissing } from "./commands/shared.ts";
32
41
  import { type CarrierName, CARRIERS, findRoot } from "./repository.ts";
33
42
  import { EVIDENCE_DIR, evidenceFiles, openSession, type SessionOptions } from "./session.ts";
34
43
 
@@ -50,7 +59,7 @@ commands
50
59
  graph [--format json] [--include-bodies] [--release <product>:<label>] [--out <file>]
51
60
  [--generated-at <time>] [--report evidence|knowledge|impact|ownership|all]...
52
61
  [--include-restricted]
53
- print the intentset/export/0.4 envelope, or write it to --out;
62
+ print the intentset/export/0.5 envelope, or write it to --out;
54
63
  each --report adds a section (ownership needs L2, evidence L3; all
55
64
  is every one the level reads). Restricted artifacts are withheld
56
65
  and counted unless --include-restricted
@@ -69,6 +78,19 @@ commands
69
78
  exits 1 when any needs a run
70
79
  impact <ID> what depends on an artifact: direct, candidates, review context,
71
80
  ancestors
81
+ list [--id <ID|prefix*>] [--type <t>] [--status <s>] [--delivery <d>] [--owner <o>]
82
+ [--audience <a>] [--visibility <v>] [--under <ID>] [--product <ID>] [--release <label>]
83
+ [--not-release <label>] [--role <r>] [--edition <e>] [--flag <f>]
84
+ [--has-extension <key>] [--has [some:|none:|all:]<out|in>:<kind>[=<status>,...]]
85
+ [--describing <slice>] [--claims <path>] [--where <file.json>] [--from <export.json>]
86
+ [--include-restricted] [--count-by <field>] [--check] [--format table|json|ids]
87
+ the records that match: fields combine with AND, the values of one
88
+ field (comma-separated or repeated) with OR, and matching is exact
89
+ (a trailing * on an ID is a prefix). --where gives the selector as
90
+ JSON instead; --from reads an export, not the tree. A record whose
91
+ answer depends on a withheld link is undetermined, listed apart.
92
+ Never writes; exits 0 whenever it answered, and with --check 1 when
93
+ any record matched or is undetermined (spec/list.md)
72
94
  context <ID|path> [--include-restricted]
73
95
  the bounded engineering context of an artifact, with paths and
74
96
  bodies; given a file a slice claims, the context of that slice
@@ -126,7 +148,8 @@ options
126
148
 
127
149
  exit codes: 0 no errors, 1 validation errors (or, with review --fail-on-drift,
128
150
  unacknowledged drift; with --fail-on-baseline-growth, a baseline
129
- entry the base did not hold), 2 invocation or tool failure`;
151
+ entry the base did not hold; with list --check, a record matched or
152
+ undetermined), 2 invocation or tool failure`;
130
153
 
131
154
  const COMMANDS = [
132
155
  "init",
@@ -137,6 +160,7 @@ const COMMANDS = [
137
160
  "evidence",
138
161
  "impact",
139
162
  "context",
163
+ "list",
140
164
  "review",
141
165
  "publish",
142
166
  "serve",
@@ -182,6 +206,7 @@ const COMMAND_OPTIONS: Record<string, string[]> = {
182
206
  ],
183
207
  impact: LEVEL_OPTIONS,
184
208
  context: ["include-restricted", ...LEVEL_OPTIONS],
209
+ list: [...SELECTOR_OPTIONS, "where", "from", "count-by", "check", "format", "include-restricted"],
185
210
  review: ["base", "fail-on-drift", "fail-on-baseline-growth", ...LEVEL_OPTIONS],
186
211
  serve: ["port", "host", "out", ...LEVEL_OPTIONS],
187
212
  mcp: [
@@ -223,6 +248,21 @@ const POSITIONALS: Record<string, { subs?: Record<string, number>; count: number
223
248
  };
224
249
 
225
250
  export async function main(argv: string[], io: Io): Promise<number> {
251
+ try {
252
+ return await dispatch(argv, io);
253
+ } catch (error) {
254
+ // The one failure a command raises rather than reports: the scanner's TypeScript is not there. It arrives from
255
+ // the first check that needs it (architecture check, validate at L2 and above, review, a subject-bound
256
+ // evidence read), and every other command has already run without it.
257
+ if (error instanceof TypeScriptMissingError) {
258
+ io.stderr(`${typescriptMissing(error, argv)}\n`);
259
+ return 2;
260
+ }
261
+ throw error;
262
+ }
263
+ }
264
+
265
+ async function dispatch(argv: string[], io: Io): Promise<number> {
226
266
  let parsed: ReturnType<typeof parse>;
227
267
  try {
228
268
  parsed = parse(argv);
@@ -336,7 +376,7 @@ export async function main(argv: string[], io: Io): Promise<number> {
336
376
 
337
377
  // Evidence scope: --product with --release, or graph's --release <product>:<label>.
338
378
  let scope: RunScope | null = null;
339
- if ((command !== "evidence" || sub === "stale") && command !== "publish" && command !== "mcp") {
379
+ if ((command !== "evidence" || sub === "stale") && command !== "publish" && command !== "mcp" && command !== "list") {
340
380
  if (command === "graph" || command === "evidence") {
341
381
  const release = values.release === undefined ? null : parseRelease(values.release);
342
382
  scope = release === null ? null : { product: release.product, release: release.label };
@@ -425,6 +465,32 @@ export async function main(argv: string[], io: Io): Promise<number> {
425
465
  const session = openSession(command, sessionOptions(levelGiven ?? "L1"), io);
426
466
  return typeof session === "number" ? session : impactCommand(session, args[0], values.json, io);
427
467
  }
468
+ case "list": {
469
+ const options: ListOptions = {
470
+ given: tokens.flatMap((token) =>
471
+ token.kind === "option" && SELECTOR_OPTIONS.includes(token.name)
472
+ ? [{ name: token.name, value: token.value ?? "" }]
473
+ : [],
474
+ ),
475
+ where: values.where,
476
+ from: values.from,
477
+ countBy: values["count-by"],
478
+ format: values.format,
479
+ json: values.json,
480
+ check: values.check,
481
+ includeRestricted: values["include-restricted"],
482
+ };
483
+ const checked = listUsageProblem(options, io);
484
+ if ("problem" in checked) return usage(checked.problem);
485
+ if (options.from !== undefined) {
486
+ const tree = (["root", "carrier", "level"] as const).find((name) => values[name] !== undefined);
487
+ if (tree !== undefined)
488
+ return usage(`--${tree} reads the tree; --from reads the records from the export alone`);
489
+ return listFromExportCommand(checked, { ...options, from: options.from }, io);
490
+ }
491
+ const session = openSession(command, sessionOptions(levelGiven ?? "L1"), io);
492
+ return typeof session === "number" ? session : listCommand(session, checked, options, io);
493
+ }
428
494
  case "context": {
429
495
  const session = openSession(command, sessionOptions(levelGiven ?? "L1"), io);
430
496
  if (typeof session === "number") return session;
@@ -567,6 +633,19 @@ function parse(argv: string[]) {
567
633
  host: { type: "string" },
568
634
  to: { type: "string" },
569
635
  "allow-dirty": { type: "boolean", default: false },
636
+ id: { type: "string", multiple: true },
637
+ type: { type: "string", multiple: true },
638
+ status: { type: "string", multiple: true },
639
+ delivery: { type: "string", multiple: true },
640
+ owner: { type: "string", multiple: true },
641
+ under: { type: "string", multiple: true },
642
+ "not-release": { type: "string", multiple: true },
643
+ has: { type: "string", multiple: true },
644
+ "has-extension": { type: "string", multiple: true },
645
+ describing: { type: "string", multiple: true },
646
+ claims: { type: "string", multiple: true },
647
+ where: { type: "string" },
648
+ "count-by": { type: "string" },
570
649
  },
571
650
  });
572
651
  }
package/src/session.ts CHANGED
@@ -48,11 +48,10 @@ import {
48
48
  import { insideRoot } from "./outputs.ts";
49
49
  import { formatDiagnostic, type Io, type Snapshot } from "./output.ts";
50
50
  import { type CarrierName, CONFIG_PATH, findRoot, loadRepository, type Repository } from "./repository.ts";
51
- import { bindingFor, subjectsOf } from "./subject.ts";
51
+ import { bindingFor, EVIDENCE_DIR, subjectsOf } from "./subject.ts";
52
52
  import { readTree, type Tree } from "./tree.ts";
53
53
 
54
- /** Where run records are read from when no --evidence is given. Local output: not to be committed. */
55
- export const EVIDENCE_DIR = ".intentset/evidence";
54
+ export { EVIDENCE_DIR } from "./subject.ts";
56
55
 
57
56
  export interface SessionOptions {
58
57
  root?: string;
@@ -290,7 +289,9 @@ export function publicationReadiness(graph: Graph): { checked: string[]; diagnos
290
289
  const review = reviewStatus(graph, artifact);
291
290
  if (review.status === "current") continue;
292
291
  const reasons: string[] = [];
293
- if (review.changed.length > 0) reasons.push(`${review.changed.join(", ")} changed since review`);
292
+ if (review.unknownHash !== null) {
293
+ reasons.push(`its review pins hashes of kind ${review.unknownHash}, which this version does not read`);
294
+ } else if (review.changed.length > 0) reasons.push(`${review.changed.join(", ")} changed since review`);
294
295
  if (review.missing.length > 0) reasons.push(`no review pin for ${review.missing.join(", ")}`);
295
296
  if (review.reviewer === null) reasons.push("it names no reviewer");
296
297
  if (review.reviewedAt === null) reasons.push("it gives no review date");
package/src/subject.ts CHANGED
@@ -21,6 +21,18 @@ import { changedSince } from "./git.ts";
21
21
  import type { Repository } from "./repository.ts";
22
22
  import type { Tree } from "./tree.ts";
23
23
 
24
+ /** Where run records are read from when no --evidence is given. Local output: not to be committed. */
25
+ export const EVIDENCE_DIR = ".intentset/evidence";
26
+
27
+ /**
28
+ * The run records under EVIDENCE_DIR are the toolchain's own output, never part of a subject, so a change to them
29
+ * says nothing about evidence and is left out of what `changedSince` answers: EVID005 would otherwise list the file
30
+ * `evidence import --out` has just written, until it is ignored or committed.
31
+ */
32
+ function outsideEvidence(paths: string[] | null): string[] | null {
33
+ return paths === null ? null : paths.filter((path) => path !== EVIDENCE_DIR && !path.startsWith(`${EVIDENCE_DIR}/`));
34
+ }
35
+
24
36
  /** The binding in force: the option, else the configuration, else the default (Core §8). */
25
37
  export function bindingFor(repo: Repository, graph: Graph, given: EvidenceBinding | undefined): BindingDecision {
26
38
  return resolveBinding(given ?? repo.config.evidence.binding, hasSliceClaims(graph));
@@ -56,7 +68,7 @@ export function subjectsOf(repo: Repository, graph: Graph, tree: Tree): Subjects
56
68
  subjects: subjectsFor(graph, source),
57
69
  source,
58
70
  changedSince: (commit) => {
59
- if (!asked.has(commit)) asked.set(commit, changedSince(repo.root, commit));
71
+ if (!asked.has(commit)) asked.set(commit, outsideEvidence(changedSince(repo.root, commit)));
60
72
  return asked.get(commit) ?? null;
61
73
  },
62
74
  };
package/dist/slices.d.ts DELETED
@@ -1,24 +0,0 @@
1
- /**
2
- * Slices, the code they claim and the records that describe them, for
3
- * `review`'s list of slices whose code changed while their records did not
4
- * (Core §10) and for `context <path>`.
5
- *
6
- * A slice's code is what its `source`, `backend` and `contract` claims match;
7
- * its `verification` and `documentation` claims are checks and prose, not
8
- * implementation. The records that describe a slice are the slice itself, the
9
- * behaviors it implements, the rules that govern them, the scenarios that
10
- * illustrate them, the verifications of any of those, the contracts it
11
- * exposes, and the decisions that inform the slice, its behaviors or its
12
- * contracts. Knowledge is left out: it explains the model to an audience, and
13
- * review pins already mark it for review when the model changes.
14
- */
15
- import { type Artifact, type Graph } from "@intentset/core";
16
- /** Every slice that is not retired and has slice metadata, sorted by ID. */
17
- export declare function activeSlices(graph: Graph): Artifact[];
18
- /** The slices with any claim matching `path`, sorted by ID. */
19
- export declare function claimingSlices(graph: Graph, path: string): Artifact[];
20
- /** True when one of the slice's source, backend or contract claims matches `path`. */
21
- export declare function isSliceCode(slice: Artifact, path: string): boolean;
22
- /** The IDs of the records that describe a slice, the slice included, sorted. Only IDs present in the graph. */
23
- export declare function describingRecords(graph: Graph, slice: Artifact): string[];
24
- //# sourceMappingURL=slices.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"slices.d.ts","sourceRoot":"","sources":["../src/slices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAkB,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAK5E,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,CAIrD;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAErE;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,+GAA+G;AAC/G,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM,EAAE,CAgBzE"}
package/dist/slices.js DELETED
@@ -1,48 +0,0 @@
1
- /**
2
- * Slices, the code they claim and the records that describe them, for
3
- * `review`'s list of slices whose code changed while their records did not
4
- * (Core §10) and for `context <path>`.
5
- *
6
- * A slice's code is what its `source`, `backend` and `contract` claims match;
7
- * its `verification` and `documentation` claims are checks and prose, not
8
- * implementation. The records that describe a slice are the slice itself, the
9
- * behaviors it implements, the rules that govern them, the scenarios that
10
- * illustrate them, the verifications of any of those, the contracts it
11
- * exposes, and the decisions that inform the slice, its behaviors or its
12
- * contracts. Knowledge is left out: it explains the model to an audience, and
13
- * review pins already mark it for review when the model changes.
14
- */
15
- import { compareStrings } from "@intentset/core";
16
- import { matchGlob } from "./glob.js";
17
- const CODE_CLAIMS = new Set(["source", "backend", "contract"]);
18
- /** Every slice that is not retired and has slice metadata, sorted by ID. */
19
- export function activeSlices(graph) {
20
- return [...graph.artifacts.values()]
21
- .filter((a) => a.meta.type === "slice" && a.meta.status !== "retired" && a.meta.slice !== undefined)
22
- .sort((a, b) => compareStrings(a.meta.id, b.meta.id));
23
- }
24
- /** The slices with any claim matching `path`, sorted by ID. */
25
- export function claimingSlices(graph, path) {
26
- return activeSlices(graph).filter((slice) => slice.meta.slice?.claims.some((claim) => matchGlob(claim.path, path)));
27
- }
28
- /** True when one of the slice's source, backend or contract claims matches `path`. */
29
- export function isSliceCode(slice, path) {
30
- return slice.meta.slice?.claims.some((claim) => CODE_CLAIMS.has(claim.kind) && matchGlob(claim.path, path)) ?? false;
31
- }
32
- /** The IDs of the records that describe a slice, the slice included, sorted. Only IDs present in the graph. */
33
- export function describingRecords(graph, slice) {
34
- const out = new Set([slice.meta.id]);
35
- const targets = (id, kind) => (graph.out.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.to);
36
- const sources = (id, kind) => (graph.in.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.from);
37
- const behaviors = targets(slice.meta.id, "implements");
38
- const rules = behaviors.flatMap((id) => targets(id, "governedBy"));
39
- const scenarios = behaviors.flatMap((id) => sources(id, "illustrates"));
40
- const claims = [...behaviors, ...rules, ...scenarios];
41
- const verifications = claims.flatMap((id) => sources(id, "verifies"));
42
- const contracts = targets(slice.meta.id, "exposes");
43
- const decisions = [slice.meta.id, ...behaviors, ...contracts].flatMap((id) => targets(id, "informedBy"));
44
- for (const id of [...claims, ...verifications, ...contracts, ...decisions])
45
- out.add(id);
46
- return [...out].filter((id) => graph.artifacts.has(id)).sort(compareStrings);
47
- }
48
- //# sourceMappingURL=slices.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"slices.js","sourceRoot":"","sources":["../src/slices.ts"],"names":[],"mappings":"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"}
package/src/slices.ts DELETED
@@ -1,54 +0,0 @@
1
- /**
2
- * Slices, the code they claim and the records that describe them, for
3
- * `review`'s list of slices whose code changed while their records did not
4
- * (Core §10) and for `context <path>`.
5
- *
6
- * A slice's code is what its `source`, `backend` and `contract` claims match;
7
- * its `verification` and `documentation` claims are checks and prose, not
8
- * implementation. The records that describe a slice are the slice itself, the
9
- * behaviors it implements, the rules that govern them, the scenarios that
10
- * illustrate them, the verifications of any of those, the contracts it
11
- * exposes, and the decisions that inform the slice, its behaviors or its
12
- * contracts. Knowledge is left out: it explains the model to an audience, and
13
- * review pins already mark it for review when the model changes.
14
- */
15
- import { type Artifact, compareStrings, type Graph } from "@intentset/core";
16
- import { matchGlob } from "./glob.ts";
17
-
18
- const CODE_CLAIMS = new Set(["source", "backend", "contract"]);
19
-
20
- /** Every slice that is not retired and has slice metadata, sorted by ID. */
21
- export function activeSlices(graph: Graph): Artifact[] {
22
- return [...graph.artifacts.values()]
23
- .filter((a) => a.meta.type === "slice" && a.meta.status !== "retired" && a.meta.slice !== undefined)
24
- .sort((a, b) => compareStrings(a.meta.id, b.meta.id));
25
- }
26
-
27
- /** The slices with any claim matching `path`, sorted by ID. */
28
- export function claimingSlices(graph: Graph, path: string): Artifact[] {
29
- return activeSlices(graph).filter((slice) => slice.meta.slice?.claims.some((claim) => matchGlob(claim.path, path)));
30
- }
31
-
32
- /** True when one of the slice's source, backend or contract claims matches `path`. */
33
- export function isSliceCode(slice: Artifact, path: string): boolean {
34
- return slice.meta.slice?.claims.some((claim) => CODE_CLAIMS.has(claim.kind) && matchGlob(claim.path, path)) ?? false;
35
- }
36
-
37
- /** The IDs of the records that describe a slice, the slice included, sorted. Only IDs present in the graph. */
38
- export function describingRecords(graph: Graph, slice: Artifact): string[] {
39
- const out = new Set<string>([slice.meta.id]);
40
- const targets = (id: string, kind: string) =>
41
- (graph.out.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.to);
42
- const sources = (id: string, kind: string) =>
43
- (graph.in.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.from);
44
-
45
- const behaviors = targets(slice.meta.id, "implements");
46
- const rules = behaviors.flatMap((id) => targets(id, "governedBy"));
47
- const scenarios = behaviors.flatMap((id) => sources(id, "illustrates"));
48
- const claims = [...behaviors, ...rules, ...scenarios];
49
- const verifications = claims.flatMap((id) => sources(id, "verifies"));
50
- const contracts = targets(slice.meta.id, "exposes");
51
- const decisions = [slice.meta.id, ...behaviors, ...contracts].flatMap((id) => targets(id, "informedBy"));
52
- for (const id of [...claims, ...verifications, ...contracts, ...decisions]) out.add(id);
53
- return [...out].filter((id) => graph.artifacts.has(id)).sort(compareStrings);
54
- }