@afokapu/atdd-bun 0.10.33 → 0.10.34
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.
|
@@ -8,11 +8,12 @@ statement: >-
|
|
|
8
8
|
terms:
|
|
9
9
|
- term_id: presets
|
|
10
10
|
text: >-
|
|
11
|
-
Planning: planner (plus docs when the repository keeps documentation under docs/). Building, once RED starts: add coder, tester, traceability, security; add htmx and design for a web UI, and interlocking once trains have a runtime. Instrumenting: add telemetry.
|
|
11
|
+
Planning: planner (plus docs when the repository keeps documentation under docs/). Building, once RED starts: add coder, tester, traceability, security; add htmx and design for a web UI, and interlocking once trains have a runtime. Instrumenting: add telemetry. Coordinating durable multi-agent work: after @afokapu/atdd-workflow is installed locally, add workflow.
|
|
12
12
|
values:
|
|
13
13
|
planning: [planner]
|
|
14
14
|
building: [coder, tester, traceability, security]
|
|
15
15
|
instrumenting: [telemetry]
|
|
16
|
+
coordinating: [workflow]
|
|
16
17
|
- term_id: profile_registries
|
|
17
18
|
text: >-
|
|
18
19
|
A deterministic, profile-scoped projection of the detector manifests, convention paths, and
|
|
@@ -32,6 +33,7 @@ terms:
|
|
|
32
33
|
tester: conventions/_profiles/tester.yaml
|
|
33
34
|
topology: conventions/_profiles/topology.yaml
|
|
34
35
|
traceability: conventions/_profiles/traceability.yaml
|
|
36
|
+
workflow: conventions/_profiles/workflow.yaml
|
|
35
37
|
content:
|
|
36
38
|
summary: >-
|
|
37
39
|
A goal is easier to name than a profile. The presets map what the user is doing to the profiles that judge it, in the order the work creates what they check.
|
|
@@ -39,8 +41,9 @@ content:
|
|
|
39
41
|
planner alone keeps the plan consistent: it parses, validates and resolves the plan with nothing else present. traceability judges the links from the plan to tests and source, so before tests exist it can only report that every acceptance is untested; it joins with building, and from then on it also catches a plan change that orphans a test.
|
|
40
42
|
security joins with the first code, not at release: a secret committed during GREEN stays in history.
|
|
41
43
|
coder already bundles the architecture, metrics, runtime, design and responsive detectors, so those need no later preset.
|
|
44
|
+
workflow is optional: it governs durable seats, tasks and threads only when the repository has the locally installed @afokapu/atdd-workflow companion. It is deliberately not selected by a default profile list.
|
|
42
45
|
fix_hint: |
|
|
43
|
-
At a stage whose preset is missing, ask what the user wants to do (planning, building, or
|
|
46
|
+
At a stage whose preset is missing, ask what the user wants to do (planning, building, instrumenting, or coordinating), recommend that preset and say it can be adopted step by step, then write the chosen list to profiles: in atdd-bun.yaml.
|
|
44
47
|
exceptions:
|
|
45
48
|
- >-
|
|
46
49
|
A profile can always be run by name to see what it would report (atdd-bun coder); only profiles: decides what the repository is governed by.
|
package/integrity.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.10.
|
|
2
|
+
"version": "0.10.34",
|
|
3
3
|
"files": {
|
|
4
4
|
"HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
|
|
5
5
|
"PLANNER_PORT.md": "fb5935bac8b7ac18994de21e43ace3a5ef8cd55f85b0e3349fca261280054f11",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"conventions/_profiles/tester.yaml": "348183eee5298cbb843031f6c41c3f9d0a725b11b3c66e154b47c9b340a72666",
|
|
20
20
|
"conventions/_profiles/topology.yaml": "08df698c2e5419e8f7faa83f5f15c3ba9c085cc2ce72579624eb24962a6b8612",
|
|
21
21
|
"conventions/_profiles/traceability.yaml": "adce93fb8392307685a01bec85e60534c59a9f4f0e4f8ea4767d79d22cf80878",
|
|
22
|
+
"conventions/_profiles/workflow.yaml": "162404ac861c77d1d4f4f2a8b731eb80f75677197636f96d198909661247b229",
|
|
22
23
|
"conventions/atdd-bun.planner/atdd-bun.planner.acceptance-identity.convention.yaml": "81c5c773d5ee15e8d99a2c237aa9846b110b2533df45ebf407d0e3ec6ed3dd03",
|
|
23
24
|
"conventions/atdd-bun.planner/atdd-bun.planner.identity-required.convention.yaml": "e96d7c1455d0c221072d82e7f2b55da1c4f9ceb17718eb6ed96affdeef37675b",
|
|
24
25
|
"conventions/atdd-bun.planner/atdd-bun.planner.identity-unique.convention.yaml": "409ba1d035ed46bc45f3627b62fd3a6a5967d984cc4c071c41be3f3fa887c6d6",
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
"conventions/atdd-bun.planner/atdd-bun.planner.reference-resolves.convention.yaml": "b96927d269692c29c02fee38ecbeb999480791f8dc7486c4072a9ff30183d7d6",
|
|
28
29
|
"conventions/atdd-bun.planner/atdd-bun.planner.schema.convention.yaml": "16d0a26451758b759e6ca1f5b4f38ae1feff129688306ffcc2f70f6d3974b591",
|
|
29
30
|
"conventions/atdd-bun.planner/atdd-bun.planner.train-wagon-resolves.convention.yaml": "6410e248351e701dcba38c671c07c4d8f6d67927984ed4d18e567abd1eb1f3f0",
|
|
30
|
-
"conventions/atdd-bun.profiles/atdd-bun.profiles.presets.convention.yaml": "
|
|
31
|
+
"conventions/atdd-bun.profiles/atdd-bun.profiles.presets.convention.yaml": "7ef8f1bc1a9c148f93a1929dbb854c467a508a51a7c7b2b3f49c245fd1c0c734",
|
|
31
32
|
"conventions/atdd-bun.topology/atdd-bun.topology.e2e-location.convention.yaml": "3a3b991bc851b86d597636200f5cb10a0defda3920348860cdca28955ab91f4c",
|
|
32
33
|
"conventions/atdd-bun.topology/atdd-bun.topology.feature-source-coverage.convention.yaml": "2b64093f1e7f1fe0e2edcadf12e560fa80cecec19536abaa5bf09af362c6694e",
|
|
33
34
|
"conventions/atdd-bun.topology/atdd-bun.topology.feature-test-coverage.convention.yaml": "279be57399c9b79a1bd758414076e3053d67b7d4a7fcbbb950ea12d1201effeb",
|
|
@@ -1194,7 +1195,7 @@
|
|
|
1194
1195
|
"src/ci.ts": "67e54d2cbfc44a9e42837d5e750af526d6255c539d3c452a4d5fca3226c0d9ac",
|
|
1195
1196
|
"src/cli.ts": "cbc50f1e7fc7d0935f78230b7da6e232ed7ee6f04828e015c599fb6f6edcbfa2",
|
|
1196
1197
|
"src/docs-capability.ts": "bdeeb414649049b1cd4fa8008cada3a576318be51c43bec91bfa46a8e250c270",
|
|
1197
|
-
"src/enforce.ts": "
|
|
1198
|
+
"src/enforce.ts": "8b22a3ac30fdd35c9caa4c23f064498293a628a342d590fca1f2040ee3a8ea05",
|
|
1198
1199
|
"src/hooks.ts": "0b4ca8c857e637bbd144e737e78eda924e3cba7b038c0abe5e6916d200d73068",
|
|
1199
1200
|
"src/index.ts": "4081f8b08d1d3ee9e15a9964143e53d9cfb94a5616ab3e58a180d24cc16dc381",
|
|
1200
1201
|
"src/integrity.ts": "44ebe101933e8b1a42c563acdffcaf7a24baa4861eb8731a914bc51500d0a93d",
|
|
@@ -1202,10 +1203,10 @@
|
|
|
1202
1203
|
"src/planner-kernel.ts": "5d3f5305fb59becb2cc97d8a03aca6b104f385805d91cc0410856e289dd6fbb1",
|
|
1203
1204
|
"src/planner-schema-validator.ts": "ce529c0936171075e93c7539b3655fe895a98dd2973ebda5775958e6ebc316d9",
|
|
1204
1205
|
"src/planner-validators.ts": "99adeb65f579af923c11f21a8f55cfb9993b007fd8df59c5f764696b55379cfc",
|
|
1205
|
-
"src/profile-registry.ts": "
|
|
1206
|
+
"src/profile-registry.ts": "716ef6e75e8e6eb14ffee37095b4fc9b38ddba2170fb84e7f06476863c3c02a5",
|
|
1206
1207
|
"src/register.ts": "6a2f1c6b8df0dc9c8f45df8ec9fac2be29044e048b08c8ffe423410e94dadedd",
|
|
1207
1208
|
"src/release.ts": "7b0fe444ee81f476f53b9c21b6c347ef588ee616c60211419cf984132be768ba",
|
|
1208
|
-
"src/setup.ts": "
|
|
1209
|
+
"src/setup.ts": "38233265afedc63499bbef3b5403889651add335a70efd3813254ba53f4fc4fe",
|
|
1209
1210
|
"src/telemetry-plan.ts": "cdb1e8bfff414f3ea81977388973da2cf6d89567d9e060dc9d5989a33600f153",
|
|
1210
1211
|
"src/topology.ts": "9779ee48e1c453abb76363163d4ab363463fa14e359571139f6d1f1fcc51ae2c",
|
|
1211
1212
|
"templates/agents/AGENTS.block.md": "978ea89e1469f0a89a6ef6e4ac40bdc11ae856bb2754069743758ba204c395a1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@afokapu/atdd-bun",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.34",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/afokapu/atdd-bun.git"
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"ajv-formats": "^3.0.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@afokapu/atdd-workflow": "^0.1.7",
|
|
42
43
|
"@axe-core/playwright": "^4.13.0",
|
|
43
44
|
"@playwright/test": "^1.63.0"
|
|
44
45
|
}
|
package/src/enforce.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { topologyFor } from "./topology";
|
|
6
6
|
|
|
7
|
-
export type Profile = "traceability" | "topology" | "docs" | "planner" | "telemetry" | "coder" | "tester" | "security" | "architecture" | "metrics" | "runtime" | "interlocking" | "htmx" | "design" | "all";
|
|
7
|
+
export type Profile = "traceability" | "topology" | "docs" | "planner" | "telemetry" | "coder" | "tester" | "security" | "architecture" | "metrics" | "runtime" | "interlocking" | "htmx" | "design" | "workflow" | "all";
|
|
8
8
|
export type ConcreteProfile = Exclude<Profile, "all">;
|
|
9
9
|
|
|
10
10
|
export type Violation = {
|
|
@@ -38,15 +38,28 @@ export const profileImplementations: Record<ConcreteProfile, string[]> = {
|
|
|
38
38
|
interlocking: ["bun_interlocking_binding", "bun_interlocking_coverage", "bun_interlocking_infrastructure"],
|
|
39
39
|
htmx: ["htmx_hypermedia_detector", "htmx_tester_detector", "htmx_e2e_detector"],
|
|
40
40
|
design: ["bun_design_system_detector", "bun_responsive_detector"],
|
|
41
|
+
// Operational policy only: its convention lives in the optional @afokapu/atdd-workflow package.
|
|
42
|
+
workflow: [],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Conventions which an installed companion package contributes to an otherwise policy-only profile. */
|
|
46
|
+
export const profileConventions: Record<ConcreteProfile, Array<{ rule_id: string; path: string }>> = {
|
|
47
|
+
traceability: [], topology: [], docs: [], planner: [], telemetry: [], coder: [], tester: [], security: [],
|
|
48
|
+
architecture: [], metrics: [], runtime: [], interlocking: [], htmx: [], design: [],
|
|
49
|
+
workflow: [{ rule_id: "atdd-workflow.workflow.lifecycle", path: "../atdd-workflow/conventions/atdd-workflow.workflow/atdd-workflow.workflow.lifecycle.convention.yaml" }],
|
|
41
50
|
};
|
|
42
51
|
|
|
43
52
|
/** Every profile but `all`. */
|
|
44
53
|
export const concreteProfiles = Object.keys(profileImplementations) as ConcreteProfile[];
|
|
54
|
+
/** Profiles a repository adopts only when it explicitly uses the optional companion package. */
|
|
55
|
+
export const optionalProfiles: ConcreteProfile[] = ["workflow"];
|
|
56
|
+
/** The safe default set for repositories that have not opted into an optional companion profile. */
|
|
57
|
+
export const defaultProfiles = concreteProfiles.filter(profile => !optionalProfiles.includes(profile));
|
|
45
58
|
/** Profile names accepted by the CLI and public integrations. */
|
|
46
59
|
export const profileNames = [...concreteProfiles, "all"] as Profile[];
|
|
47
60
|
|
|
48
61
|
/**
|
|
49
|
-
* The profiles the operator has activated: `profiles:` in atdd-bun.yaml, or every profile when absent. A legacy
|
|
62
|
+
* The profiles the operator has activated: `profiles:` in atdd-bun.yaml, or every built-in profile when absent. A legacy
|
|
50
63
|
* repository can adopt enforcement gradually by listing only what it is ready for. `all`, the hooks and the
|
|
51
64
|
* generated CI run exactly these; naming a profile explicitly still runs it. An unknown name or an empty list is
|
|
52
65
|
* a configuration error, never a silent "run nothing".
|
|
@@ -54,7 +67,7 @@ export const profileNames = [...concreteProfiles, "all"] as Profile[];
|
|
|
54
67
|
export async function enabledProfiles(root = process.cwd()): Promise<ConcreteProfile[]> {
|
|
55
68
|
const file = join(resolve(root), "atdd-bun.yaml");
|
|
56
69
|
const listed = existsSync(file) ? (Bun.YAML.parse(await readFile(file, "utf8")) as { profiles?: unknown } | null)?.profiles : undefined;
|
|
57
|
-
if (listed === undefined) return
|
|
70
|
+
if (listed === undefined) return defaultProfiles;
|
|
58
71
|
if (!Array.isArray(listed) || !listed.length) throw new Error("atdd-bun.yaml profiles must be a non-empty list of profile names");
|
|
59
72
|
const unknown = listed.filter(name => !concreteProfiles.includes(name));
|
|
60
73
|
if (unknown.length) throw new Error(`atdd-bun.yaml profiles lists unknown profile(s): ${unknown.join(", ")}; known: ${concreteProfiles.join(", ")}`);
|
package/src/profile-registry.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join, relative, resolve } from "node:path";
|
|
4
|
-
import { concreteProfiles, declaredRuleIds, profileImplementations } from "./enforce";
|
|
4
|
+
import { concreteProfiles, declaredRuleIds, profileConventions, profileImplementations } from "./enforce";
|
|
5
5
|
|
|
6
6
|
export const PROFILE_REGISTRY_DIR = "conventions/_profiles";
|
|
7
7
|
type Convention = { rule_id: string; path: string };
|
|
@@ -36,9 +36,9 @@ const scalar = (value: unknown) => value === null ? "null" : typeof value === "s
|
|
|
36
36
|
const fields = (value: Record<string, unknown>, indent: string) => sorted(Object.entries(value), ([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${indent}${key}: ${scalar(entry)}`);
|
|
37
37
|
const render = (profile: string, implementations: string[], conventions: Convention[], relationships: Relationship[]) => [
|
|
38
38
|
'schema_version: "1.0.0"', `profile: ${scalar(profile)}`,
|
|
39
|
-
"implementations:", ...implementations.map(implementation => ` - ${scalar(implementation)}`),
|
|
40
|
-
"conventions:", ...conventions.flatMap(convention => [` - rule_id: ${scalar(convention.rule_id)}`, ` path: ${scalar(convention.path)}`]),
|
|
41
|
-
"relationships:", ...relationships.flatMap(edge => { const entries = fields(edge, " "); return [` - ${entries[0]!.slice(4)}`, ...entries.slice(1)]; }), "",
|
|
39
|
+
implementations.length ? "implementations:" : "implementations: []", ...implementations.map(implementation => ` - ${scalar(implementation)}`),
|
|
40
|
+
conventions.length ? "conventions:" : "conventions: []", ...conventions.flatMap(convention => [` - rule_id: ${scalar(convention.rule_id)}`, ` path: ${scalar(convention.path)}`]),
|
|
41
|
+
relationships.length ? "relationships:" : "relationships: []", ...relationships.flatMap(edge => { const entries = fields(edge, " "); return [` - ${entries[0]!.slice(4)}`, ...entries.slice(1)]; }), "",
|
|
42
42
|
].join("\n");
|
|
43
43
|
|
|
44
44
|
/** Deterministically project profiles, detector manifests, conventions, and direct graph edges into small registries. */
|
|
@@ -53,9 +53,13 @@ export async function renderProfileRegistries(root = resolve(import.meta.dir, ".
|
|
|
53
53
|
const ruleIds = sorted(new Set(declared.flatMap(ids => [...ids])));
|
|
54
54
|
const missing = ruleIds.filter(id => !conventions.has(id));
|
|
55
55
|
if (missing.length) throw new Error(`${profile} declares rule IDs without exactly one convention: ${missing.join(", ")}`);
|
|
56
|
-
const
|
|
56
|
+
const supplementary = profileConventions[profile];
|
|
57
|
+
const duplicate = supplementary.find(convention => conventions.has(convention.rule_id) || ruleIds.includes(convention.rule_id));
|
|
58
|
+
if (duplicate) throw new Error(`${profile} duplicates convention ${duplicate.rule_id}`);
|
|
59
|
+
const selectedConventions = [...ruleIds.map(id => conventions.get(id)!), ...supplementary];
|
|
60
|
+
const rules = new Set(selectedConventions.map(convention => convention.rule_id));
|
|
57
61
|
const relationships = sorted(graph.edges.filter(edge => rules.has(edge.source_ref) || rules.has(edge.target_ref)), (left, right) => relationshipKey(left).localeCompare(relationshipKey(right)));
|
|
58
|
-
rendered.set(`${PROFILE_REGISTRY_DIR}/${profile}.yaml`, render(profile, implementations,
|
|
62
|
+
rendered.set(`${PROFILE_REGISTRY_DIR}/${profile}.yaml`, render(profile, implementations, selectedConventions, relationships));
|
|
59
63
|
}
|
|
60
64
|
return rendered;
|
|
61
65
|
}
|
package/src/setup.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ciInit, ciStatus } from "./ci";
|
|
|
2
2
|
import { hooksStatus, installHooks } from "./hooks";
|
|
3
3
|
import { agentInit, agentStatus } from "./agent";
|
|
4
4
|
import { integrityInit, integrityStatus } from "./integrity";
|
|
5
|
-
import {
|
|
5
|
+
import { defaultProfiles } from "./enforce";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { writeFile } from "node:fs/promises";
|
|
8
8
|
import { join } from "node:path";
|
|
@@ -14,7 +14,7 @@ import { join } from "node:path";
|
|
|
14
14
|
export async function policyInit(root = process.cwd()) {
|
|
15
15
|
const file = join(root, "atdd-bun.yaml");
|
|
16
16
|
if (existsSync(file)) return { ok: true, message: `${file} kept` };
|
|
17
|
-
await writeFile(file, `# Generated by atdd-bun init. The profiles this repository enforces; trim the list before the first commit to\n# adopt gradually. Once committed, removing a profile or the list is reported by the integrity check.\nprofiles: [${
|
|
17
|
+
await writeFile(file, `# Generated by atdd-bun init. The profiles this repository enforces; trim the list before the first commit to\n# adopt gradually. Once committed, removing a profile or the list is reported by the integrity check.\nprofiles: [${defaultProfiles.join(", ")}]\n`);
|
|
18
18
|
return { ok: true, message: file };
|
|
19
19
|
}
|
|
20
20
|
|