@bvdm/delano 0.1.7 → 0.1.8

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 (113) hide show
  1. package/.delano/README.md +7 -0
  2. package/.delano/viewer/README.md +19 -0
  3. package/.delano/viewer/public/app.js +818 -0
  4. package/.delano/viewer/public/explorer.svg +3 -0
  5. package/.delano/viewer/public/index.html +21 -0
  6. package/.delano/viewer/public/markdown.svg +6 -0
  7. package/.delano/viewer/public/styles.css +1042 -0
  8. package/.delano/viewer/public/vscode.svg +24 -0
  9. package/.delano/viewer/server.js +389 -0
  10. package/HANDBOOK.md +65 -45
  11. package/README.md +10 -2
  12. package/assets/install-manifest.json +112 -35
  13. package/assets/payload/.agents/README.md +31 -6
  14. package/assets/payload/.agents/adapters/claude/README.md +22 -3
  15. package/assets/payload/.agents/adapters/codex/README.md +22 -3
  16. package/assets/payload/.agents/adapters/opencode/README.md +22 -3
  17. package/assets/payload/.agents/adapters/pi/README.md +22 -3
  18. package/assets/payload/.agents/common/log-safety.js +55 -0
  19. package/assets/payload/.agents/eval-fixtures/skill-output/invalid/missing-evidence/output.json +6 -0
  20. package/assets/payload/.agents/eval-fixtures/skill-output/valid/summary/output.json +7 -0
  21. package/assets/payload/.agents/fixtures/github/status-snapshot.json +6 -0
  22. package/assets/payload/.agents/fixtures/linear/issue-snapshot.json +6 -0
  23. package/assets/payload/.agents/hooks/bash-worktree-fix.sh +2 -1
  24. package/assets/payload/.agents/hooks/post-tool-logger.js +2 -1
  25. package/assets/payload/.agents/hooks/user-prompt-logger.js +17 -1
  26. package/assets/payload/.agents/logs/delivery-metrics.md +22 -0
  27. package/assets/payload/.agents/logs/schema.md +20 -1
  28. package/assets/payload/.agents/rules/delivery-modes.md +17 -0
  29. package/assets/payload/.agents/schemas/README.md +22 -0
  30. package/assets/payload/.agents/schemas/artifact-scope.json +237 -0
  31. package/assets/payload/.agents/schemas/artifacts/context.schema.json +11 -0
  32. package/assets/payload/.agents/schemas/artifacts/decision_log.schema.json +12 -0
  33. package/assets/payload/.agents/schemas/artifacts/evidence.schema.json +17 -0
  34. package/assets/payload/.agents/schemas/artifacts/plan.schema.json +83 -0
  35. package/assets/payload/.agents/schemas/artifacts/spec.schema.json +101 -0
  36. package/assets/payload/.agents/schemas/artifacts/task.schema.json +121 -0
  37. package/assets/payload/.agents/schemas/artifacts/update.schema.json +12 -0
  38. package/assets/payload/.agents/schemas/artifacts/workstream.schema.json +66 -0
  39. package/assets/payload/.agents/schemas/evidence-map.json +53 -0
  40. package/assets/payload/.agents/schemas/learning/closeout-learning-proposal.schema.json +20 -0
  41. package/assets/payload/.agents/schemas/learning/delivery-metric-event.schema.json +21 -0
  42. package/assets/payload/.agents/schemas/leases/lease.schema.json +39 -0
  43. package/assets/payload/.agents/schemas/metrics/delivery-event.schema.json +29 -0
  44. package/assets/payload/.agents/schemas/metrics/delivery-events.schema.json +49 -0
  45. package/assets/payload/.agents/schemas/operating-modes.json +42 -0
  46. package/assets/payload/.agents/schemas/status-transitions.json +31 -0
  47. package/assets/payload/.agents/schemas/sync/drift-report.schema.json +25 -0
  48. package/assets/payload/.agents/schemas/sync/drift-taxonomy.json +38 -0
  49. package/assets/payload/.agents/schemas/sync/sync-map.schema.json +39 -0
  50. package/assets/payload/.agents/scripts/README.md +1 -0
  51. package/assets/payload/.agents/scripts/audit-context-files.mjs +54 -0
  52. package/assets/payload/.agents/scripts/audit-context-scoring.mjs +14 -0
  53. package/assets/payload/.agents/scripts/build-drift-report.mjs +133 -0
  54. package/assets/payload/.agents/scripts/check-artifact-schemas.mjs +116 -0
  55. package/assets/payload/.agents/scripts/check-closeout-learning-proposals.mjs +23 -0
  56. package/assets/payload/.agents/scripts/check-context-audit.mjs +61 -0
  57. package/assets/payload/.agents/scripts/check-delivery-metric-events.mjs +35 -0
  58. package/assets/payload/.agents/scripts/check-delivery-metrics.mjs +52 -0
  59. package/assets/payload/.agents/scripts/check-evidence-map.mjs +143 -0
  60. package/assets/payload/.agents/scripts/check-github-status-inspection.mjs +93 -0
  61. package/assets/payload/.agents/scripts/check-github-sync.mjs +159 -0
  62. package/assets/payload/.agents/scripts/check-handoff-summaries.mjs +57 -0
  63. package/assets/payload/.agents/scripts/check-lease-conflicts.mjs +24 -0
  64. package/assets/payload/.agents/scripts/check-lease-contracts.mjs +17 -0
  65. package/assets/payload/.agents/scripts/check-linear-issue-inspection.mjs +63 -0
  66. package/assets/payload/.agents/scripts/check-local-sync-map.mjs +151 -0
  67. package/assets/payload/.agents/scripts/check-log-safety.sh +62 -0
  68. package/assets/payload/.agents/scripts/check-operating-modes.mjs +99 -0
  69. package/assets/payload/.agents/scripts/check-path-standards.sh +1 -1
  70. package/assets/payload/.agents/scripts/check-skill-output-evals.mjs +13 -0
  71. package/assets/payload/.agents/scripts/check-status-transitions.mjs +169 -0
  72. package/assets/payload/.agents/scripts/check-strict-fixtures.mjs +140 -0
  73. package/assets/payload/.agents/scripts/check-sync-schemas.mjs +52 -0
  74. package/assets/payload/.agents/scripts/check-text-safety.mjs +158 -0
  75. package/assets/payload/.agents/scripts/check-worktree-health.mjs +100 -0
  76. package/assets/payload/.agents/scripts/fix-path-standards.sh +1 -1
  77. package/assets/payload/.agents/scripts/inspect-github-sync.mjs +108 -0
  78. package/assets/payload/.agents/scripts/lease-manager.mjs +88 -0
  79. package/assets/payload/.agents/scripts/log-event.js +3 -0
  80. package/assets/payload/.agents/scripts/plan-sync-repairs.mjs +66 -0
  81. package/assets/payload/.agents/scripts/pm/validate.sh +656 -2
  82. package/assets/payload/.agents/scripts/propose-closeout-learning.mjs +20 -0
  83. package/assets/payload/.agents/scripts/read-local-sync-map.mjs +135 -0
  84. package/assets/payload/.agents/scripts/select-next-task.mjs +22 -0
  85. package/assets/payload/.agents/scripts/summarize-project-metrics.mjs +15 -0
  86. package/assets/payload/.agents/skills/closeout-skill/SKILL.md +3 -0
  87. package/assets/payload/.agents/skills/closeout-skill/references/runbook.md +5 -2
  88. package/assets/payload/.agents/skills/closeout-skill/templates/closure-checklist.md +2 -0
  89. package/assets/payload/.agents/skills/closeout-skill/templates/learning-proposal.md +21 -0
  90. package/assets/payload/.agents/skills/closeout-skill/templates/learning-proposals.md +25 -0
  91. package/assets/payload/.agents/validation-fixtures/strict/invalid/broken-dependencies/dependency.md +18 -0
  92. package/assets/payload/.agents/validation-fixtures/strict/invalid/broken-dependencies/task.md +24 -0
  93. package/assets/payload/.agents/validation-fixtures/strict/invalid/invalid-transition/task.md +20 -0
  94. package/assets/payload/.agents/validation-fixtures/strict/invalid/missing-evidence/task.md +27 -0
  95. package/assets/payload/.agents/validation-fixtures/strict/invalid/path-leak/task.md +27 -0
  96. package/assets/payload/.agents/validation-fixtures/strict/invalid/stale-context/context.md +9 -0
  97. package/assets/payload/.agents/validation-fixtures/strict/manifest.json +11 -0
  98. package/assets/payload/.agents/validation-fixtures/strict/valid/minimal-project/task.md +27 -0
  99. package/assets/payload/.delano/viewer/README.md +19 -0
  100. package/assets/payload/.delano/viewer/public/app.js +818 -0
  101. package/assets/payload/.delano/viewer/public/explorer.svg +3 -0
  102. package/assets/payload/.delano/viewer/public/index.html +21 -0
  103. package/assets/payload/.delano/viewer/public/markdown.svg +6 -0
  104. package/assets/payload/.delano/viewer/public/styles.css +1042 -0
  105. package/assets/payload/.delano/viewer/public/vscode.svg +24 -0
  106. package/assets/payload/.delano/viewer/server.js +389 -0
  107. package/assets/payload/.project/templates/plan.md +1 -1
  108. package/assets/payload/.project/templates/spec.md +1 -1
  109. package/assets/payload/.project/templates/task.md +1 -0
  110. package/assets/payload/HANDBOOK.md +65 -45
  111. package/package.json +31 -2
  112. package/src/cli/commands/viewer.js +81 -0
  113. package/src/cli/index.js +8 -0
@@ -0,0 +1,20 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename);
5
+ const repoRoot = resolveRepoRoot(__dirname);
6
+ const project = readOption("--project") || "delano-learning-loop";
7
+ const metricsPath = readOption("--metrics") || path.join(repoRoot, ".agents", "metrics", "delivery-events.jsonl");
8
+ const metrics = readEvents(metricsPath).filter((event)=>event.project === project || !event.project);
9
+ const proposal = {
10
+ schema_version: 1,
11
+ mode: "dry-run-proposal",
12
+ project,
13
+ apply_posture: "proposal-only-no-mutation",
14
+ summary: { event_count: metrics.length, recommendation_count: 1, privacy: "summary-only" },
15
+ recommendations: [{ id: "LP-001", type: "closeout-learning", summary: metrics.length ? "Review summarized delivery metrics during closeout." : "Capture at least one delivery metric event during future closeouts.", evidence: ["scripts/summarize-project-metrics.mjs", ".agents/logs/delivery-metrics.md"] }]
16
+ };
17
+ if (process.argv.includes("--json")) console.log(JSON.stringify(proposal,null,2)); else console.log(`Closeout learning proposal produced ${proposal.summary.recommendation_count} recommendation(s) for ${project}.`);
18
+ function readEvents(filePath){ if(!existsSync(filePath)) return []; return readFileSync(filePath,"utf8").split(/\r?\n/).filter(Boolean).map(line=>JSON.parse(line)); }
19
+ function readOption(name){ const i=process.argv.indexOf(name); return i===-1?"":process.argv[i+1]; }
20
+ function resolveRepoRoot(startDir){ for(const c of [path.resolve(startDir,".."),path.resolve(startDir,"..","..")]) if(existsSync(path.join(c,".agents"))) return c; return path.resolve(startDir,".."); }
@@ -0,0 +1,135 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const repoRoot = resolveRepoRoot(__dirname);
8
+
9
+ export function readLocalSyncMap(root = repoRoot) {
10
+ const projectsRoot = path.join(root, ".project", "projects");
11
+ const registryPath = path.join(root, ".project", "registry", "linear-map.json");
12
+ const registry = readOptionalJson(registryPath) || { projects: {}, tasks: {} };
13
+ const projects = [];
14
+
15
+ for (const projectDir of listProjectDirs(projectsRoot)) {
16
+ const slug = path.basename(projectDir);
17
+ const projectRegistry = registry.projects?.[slug] || {};
18
+ const tasks = [];
19
+ const tasksDir = path.join(projectDir, "tasks");
20
+ if (existsSync(tasksDir)) {
21
+ for (const taskFile of readdirSync(tasksDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => path.join(tasksDir, entry.name)).sort()) {
22
+ const text = readFileSync(taskFile, "utf8");
23
+ const fm = parseFrontmatter(text);
24
+ const registryKey = `${slug}/${fm.id || path.basename(taskFile, ".md")}`;
25
+ const taskRegistry = registry.tasks?.[registryKey] || registry.tasks?.[fm.id] || {};
26
+ tasks.push({
27
+ local_id: fm.id || path.basename(taskFile, ".md"),
28
+ name: fm.name || titleFromMarkdown(text) || path.basename(taskFile, ".md"),
29
+ status: fm.status || "unknown",
30
+ workstream: fm.workstream || "",
31
+ local_path: toRepoPath(root, taskFile),
32
+ depends_on: parseList(fm.depends_on || "[]"),
33
+ linear_issue_id: emptyToUndefined(fm.linear_issue_id) || taskRegistry.linear_issue_id,
34
+ github_issue: emptyToUndefined(fm.github_issue) || taskRegistry.github_issue,
35
+ github_pr: emptyToUndefined(fm.github_pr) || taskRegistry.github_pr
36
+ });
37
+ }
38
+ }
39
+ projects.push({
40
+ slug,
41
+ local_path: toRepoPath(root, projectDir),
42
+ linear_project_id: projectRegistry.linear_project_id,
43
+ github_repo: projectRegistry.github_repo,
44
+ tasks
45
+ });
46
+ }
47
+
48
+ return { schema_version: 1, source: "local", projects };
49
+ }
50
+
51
+ export function validateLocalSyncMap(syncMap) {
52
+ const errors = [];
53
+ const seenProjects = new Set();
54
+ for (const project of syncMap.projects || []) {
55
+ if (seenProjects.has(project.slug)) errors.push(`duplicate project slug: ${project.slug}`);
56
+ seenProjects.add(project.slug);
57
+ if (!/^\.project\/projects\/[^/]+$/.test(project.local_path || "")) errors.push(`invalid project local_path for ${project.slug}: ${project.local_path}`);
58
+ const seenTasks = new Set();
59
+ for (const task of project.tasks || []) {
60
+ if (!/^T-[0-9]{3}$/.test(task.local_id || "")) errors.push(`${project.slug} has invalid task id: ${task.local_id}`);
61
+ if (seenTasks.has(task.local_id)) errors.push(`${project.slug} has duplicate task id: ${task.local_id}`);
62
+ seenTasks.add(task.local_id);
63
+ for (const dependency of task.depends_on || []) {
64
+ if (!seenTasks.has(dependency) && !(project.tasks || []).some((candidate) => candidate.local_id === dependency)) {
65
+ errors.push(`${project.slug}/${task.local_id} depends on missing local task ${dependency}`);
66
+ }
67
+ }
68
+ if (task.github_issue && !isUrlOrNumber(task.github_issue)) errors.push(`${project.slug}/${task.local_id} has invalid github_issue: ${task.github_issue}`);
69
+ if (task.github_pr && !isUrlOrNumber(task.github_pr)) errors.push(`${project.slug}/${task.local_id} has invalid github_pr: ${task.github_pr}`);
70
+ }
71
+ }
72
+ return errors;
73
+ }
74
+
75
+ if (import.meta.url === `file://${process.argv[1]}`) {
76
+ const syncMap = readLocalSyncMap(repoRoot);
77
+ const errors = validateLocalSyncMap(syncMap);
78
+ if (process.argv.includes("--json")) {
79
+ console.log(JSON.stringify(syncMap, null, 2));
80
+ }
81
+ if (errors.length > 0) {
82
+ console.error("Local sync map validation failed:");
83
+ for (const error of errors) console.error(`- ${error}`);
84
+ process.exit(1);
85
+ }
86
+ if (!process.argv.includes("--json")) {
87
+ const taskCount = syncMap.projects.reduce((sum, project) => sum + project.tasks.length, 0);
88
+ console.log(`Local sync map check passed for ${syncMap.projects.length} project(s) and ${taskCount} task(s).`);
89
+ }
90
+ }
91
+
92
+ function listProjectDirs(projectsRoot) {
93
+ if (!existsSync(projectsRoot)) return [];
94
+ return readdirSync(projectsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(projectsRoot, entry.name)).sort();
95
+ }
96
+ function parseFrontmatter(text) {
97
+ const match = text.match(/^---\n([\s\S]*?)\n---\n/);
98
+ if (!match) return {};
99
+ const result = {};
100
+ for (const line of match[1].split("\n")) {
101
+ const index = line.indexOf(":");
102
+ if (index === -1) continue;
103
+ result[line.slice(0, index).trim()] = line.slice(index + 1).trim();
104
+ }
105
+ return result;
106
+ }
107
+ function parseList(raw) {
108
+ const value = String(raw || "").trim();
109
+ if (!value || value === "[]") return [];
110
+ if (value.startsWith("[") && value.endsWith("]")) return value.slice(1, -1).split(",").map((item) => item.trim().replace(/^['\"]|['\"]$/g, "")).filter(Boolean);
111
+ return [value.replace(/^['\"]|['\"]$/g, "")].filter(Boolean);
112
+ }
113
+ function titleFromMarkdown(text) {
114
+ const match = text.match(/^#\s+(.+)$/m);
115
+ return match ? match[1].replace(/^Task:\s*/, "").trim() : "";
116
+ }
117
+ function emptyToUndefined(value) {
118
+ const trimmed = String(value || "").trim();
119
+ return trimmed ? trimmed : undefined;
120
+ }
121
+ function isUrlOrNumber(value) {
122
+ return /^https?:\/\//.test(value) || /^#?[0-9]+$/.test(value);
123
+ }
124
+ function readOptionalJson(filePath) {
125
+ if (!existsSync(filePath)) return null;
126
+ return JSON.parse(readFileSync(filePath, "utf8"));
127
+ }
128
+ function toRepoPath(root, filePath) {
129
+ return path.relative(root, filePath).split(path.sep).join("/");
130
+ }
131
+ function resolveRepoRoot(startDir) {
132
+ const candidates = [path.resolve(startDir, ".."), path.resolve(startDir, "..", "..")];
133
+ for (const candidate of candidates) if (existsSync(path.join(candidate, ".project")) && existsSync(path.join(candidate, ".agents"))) return candidate;
134
+ return path.resolve(startDir, "..");
135
+ }
@@ -0,0 +1,22 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = path.dirname(__filename);
6
+ const repoRoot = resolveRepoRoot(__dirname);
7
+ const projectSlug = readOption("--project") || "delano-multi-agent-execution";
8
+ const stream = readOption("--stream") || "default";
9
+ const tasksDir = path.join(repoRoot, ".project", "projects", projectSlug, "tasks");
10
+ const leases = readLeases(readOption("--leases") || path.join(repoRoot, ".agents", "leases", "active-leases.json"));
11
+ const ready = readdirSync(tasksDir).filter((file)=>file.endsWith(".md")).map((file)=>readTask(path.join(tasksDir,file))).filter((task)=>task.status === "ready");
12
+ const activeZones = new Set(leases.filter((l)=>l.status === "active" && new Date(l.expires_at).getTime() > Date.now()).flatMap((l)=>l.conflict_zones || []));
13
+ const candidates = ready.map((task)=>({ ...task, stream, blocked_by_active_zone: task.conflicts_with.some((zone)=>activeZones.has(zone)) })).filter((task)=>!task.blocked_by_active_zone);
14
+ const selected = candidates[0] || null;
15
+ const result = { schema_version: 1, project: projectSlug, stream, ready_count: ready.length, candidate_count: candidates.length, selected: selected && { id: selected.id, file: selected.file, priority: selected.priority } };
16
+ if (process.argv.includes("--json")) console.log(JSON.stringify(result, null, 2)); else console.log(selected ? `Selected ${selected.id} for ${stream}.` : `No unleased ready task for ${stream}.`);
17
+ export function readTask(filePath) { const text=readFileSync(filePath,"utf8"); return { file:path.basename(filePath), id: front(text,"id"), status: front(text,"status"), priority: front(text,"priority"), conflicts_with: list(front(text,"conflicts_with")) }; }
18
+ function front(text,key){ const m=text.match(new RegExp(`^${key}:\\s*(.*)$`,"m")); return m?m[1].trim():""; }
19
+ function list(v){ const m=v.match(/^\[(.*)\]$/); return m?m[1].split(",").map(x=>x.trim()).filter(Boolean):[]; }
20
+ function readLeases(filePath){ if(!existsSync(filePath)) return []; return JSON.parse(readFileSync(filePath,"utf8")).leases || []; }
21
+ function readOption(name){ const i=process.argv.indexOf(name); return i===-1?"":process.argv[i+1]; }
22
+ function resolveRepoRoot(startDir){ for(const c of [path.resolve(startDir,".."),path.resolve(startDir,"..","..")]) if(existsSync(path.join(c,".project"))) return c; return path.resolve(startDir,".."); }
@@ -0,0 +1,15 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename);
5
+ const repoRoot = resolveRepoRoot(__dirname);
6
+ const eventsPath = readOption("--events") || path.join(repoRoot, ".agents", "metrics", "delivery-events.jsonl");
7
+ const project = readOption("--project") || "all";
8
+ const events = readEvents(eventsPath).filter((e)=>project === "all" || e.project === project);
9
+ const byType = Object.create(null);
10
+ for (const e of events) byType[e.event_type] = (byType[e.event_type] || 0) + 1;
11
+ const summary = { schema_version: 1, project, event_count: events.length, by_type: byType, privacy: "summary-only" };
12
+ if (process.argv.includes("--json")) console.log(JSON.stringify(summary, null, 2)); else console.log(`Project metrics summary: ${events.length} event(s), privacy=summary-only.`);
13
+ function readEvents(filePath){ if(!existsSync(filePath)) return []; return readFileSync(filePath,"utf8").split(/\r?\n/).filter(Boolean).map((line)=>JSON.parse(line)); }
14
+ function readOption(name){ const i=process.argv.indexOf(name); return i===-1?"":process.argv[i+1]; }
15
+ function resolveRepoRoot(startDir){ for(const c of [path.resolve(startDir,".."),path.resolve(startDir,"..","..")]) if(existsSync(path.join(c,".agents"))) return c; return path.resolve(startDir,".."); }
@@ -17,11 +17,13 @@ description: Close the delivery loop and capture completion evidence, status upd
17
17
  - closure update
18
18
  - completion summary
19
19
  - updated status in contracts/registry
20
+ - learning proposals for any rule, skill, schema, or fixture changes discovered during closeout
20
21
 
21
22
  ## Quality checks
22
23
  - required tasks resolved
23
24
  - evidence package complete
24
25
  - outcome review captured
26
+ - learning proposals are reviewed before adoption
25
27
 
26
28
  ## Failure behavior
27
29
  - block closure when evidence is incomplete
@@ -40,3 +42,4 @@ description: Close the delivery loop and capture completion evidence, status upd
40
42
  - `references/runbook.md`
41
43
  - `templates/outcome-review.md`
42
44
  - `templates/closure-checklist.md`
45
+ - `templates/learning-proposal.md`
@@ -3,8 +3,10 @@
3
3
  1. Confirm all required tasks are in terminal state.
4
4
  2. Ensure quality evidence package is complete.
5
5
  3. Write completion summary from template.
6
- 4. Update project status and mapping registry.
7
- 5. Review event log:
6
+ 4. Draft a learning proposal for any proposed rule, skill, schema, or fixture update.
7
+ 5. Keep learning proposals in `proposed` state until reviewed and explicitly accepted.
8
+ 6. Update project status and mapping registry.
9
+ 7. Review event log:
8
10
  - `bash .agents/scripts/query-log.sh --last 100`
9
11
  6. Validate:
10
12
  - `bash .agents/scripts/pm/status.sh`
@@ -12,5 +14,6 @@
12
14
 
13
15
  Exit gate:
14
16
  - Outcome review captured
17
+ - Learning proposals reviewed before adoption
15
18
  - Evidence complete
16
19
  - Delivery state closed cleanly
@@ -4,4 +4,6 @@
4
4
  - [ ] Quality gates passed
5
5
  - [ ] Evidence package complete
6
6
  - [ ] Registry/state updated
7
+ - [ ] Learning proposals drafted for rule, skill, schema, or fixture changes
8
+ - [ ] Learning proposals reviewed before adoption
7
9
  - [ ] Retrospective scheduled
@@ -0,0 +1,21 @@
1
+ # Closeout Learning Proposal
2
+
3
+ ## Proposal Type
4
+ rule | skill | schema | fixture
5
+
6
+ ## Title
7
+
8
+ ## Rationale
9
+ What happened, what should change, and why the change is worth reviewing.
10
+
11
+ ## Target Paths
12
+ - repo/relative/path
13
+
14
+ ## Evidence
15
+ - command, task, fixture, or local event summary
16
+
17
+ ## Review Gate
18
+ Required before adoption. Do not apply the proposed rule, skill, schema, or fixture change until it has been reviewed and explicitly accepted.
19
+
20
+ ## Adoption Status
21
+ proposed
@@ -0,0 +1,25 @@
1
+ # Closeout Learning Proposals
2
+
3
+ Use this during project closeout to propose reusable runtime changes. Proposals are review-first: do not silently adopt changes to shared rules, skills, schemas, or fixtures.
4
+
5
+ ## Proposal Summary
6
+
7
+ - Project:
8
+ - Source closeout/update:
9
+ - Reviewer:
10
+ - Review status: pending
11
+
12
+ ## Proposed Changes
13
+
14
+ | Target type | Target path | Change summary | Evidence | Adoption state |
15
+ | --- | --- | --- | --- | --- |
16
+ | rule | `.agents/rules/example.md` | Replace with the observed improvement. | Link task/update evidence. | proposed |
17
+ | skill | `.agents/skills/example-skill/SKILL.md` | Replace with the observed improvement. | Link task/update evidence. | proposed |
18
+ | schema | `.agents/schemas/example.schema.json` | Replace with the observed improvement. | Link task/update evidence. | proposed |
19
+ | fixture | `.agents/validation-fixtures/example.json` | Replace with the observed improvement. | Link task/update evidence. | proposed |
20
+
21
+ ## Review Gate
22
+
23
+ - [ ] Every proposal cites observed evidence.
24
+ - [ ] No proposal is adopted before review status is approved.
25
+ - [ ] Rejected proposals retain the reason for future context.
@@ -0,0 +1,18 @@
1
+ ---
2
+ id: T-001
3
+ name: Undone dependency fixture
4
+ status: ready
5
+ workstream: WS-A
6
+ created: 2026-04-29T00:00:00Z
7
+ updated: 2026-04-29T00:10:00Z
8
+ linear_issue_id:
9
+ github_issue:
10
+ github_pr:
11
+ depends_on: []
12
+ conflicts_with: []
13
+ parallel: false
14
+ priority: high
15
+ estimate: S
16
+ ---
17
+
18
+ # Task: Undone dependency fixture
@@ -0,0 +1,24 @@
1
+ ---
2
+ id: T-002
3
+ name: Broken dependencies fixture
4
+ status: done
5
+ workstream: WS-A
6
+ created: 2026-04-29T00:00:00Z
7
+ updated: 2026-04-29T00:10:00Z
8
+ linear_issue_id:
9
+ github_issue:
10
+ github_pr:
11
+ depends_on: [T-001]
12
+ conflicts_with: []
13
+ parallel: false
14
+ priority: high
15
+ estimate: S
16
+ ---
17
+
18
+ # Task: Broken dependencies fixture
19
+
20
+ ## Acceptance Criteria
21
+ - [x] Current repo state has been inspected before implementation starts.
22
+
23
+ ## Evidence Log
24
+ - 2026-04-29T00:10:00Z: Inspected fixture and validation passed despite intentionally broken dependency.
@@ -0,0 +1,20 @@
1
+ ---
2
+ id: T-001
3
+ name: Invalid transition fixture
4
+ status: blocked
5
+ workstream: WS-A
6
+ created: 2026-04-29T00:00:00Z
7
+ updated: 2026-04-29T00:10:00Z
8
+ linear_issue_id:
9
+ github_issue:
10
+ github_pr:
11
+ depends_on: []
12
+ conflicts_with: []
13
+ parallel: false
14
+ priority: high
15
+ estimate: S
16
+ ---
17
+
18
+ # Task: Invalid transition fixture
19
+
20
+ Blocked without owner or check-back.
@@ -0,0 +1,27 @@
1
+ ---
2
+ id: T-001
3
+ name: Missing evidence fixture
4
+ status: done
5
+ workstream: WS-A
6
+ created: 2026-04-29T00:00:00Z
7
+ updated: 2026-04-29T00:10:00Z
8
+ linear_issue_id:
9
+ github_issue:
10
+ github_pr:
11
+ depends_on: []
12
+ conflicts_with: []
13
+ parallel: false
14
+ priority: high
15
+ estimate: S
16
+ ---
17
+
18
+ # Task: Missing evidence fixture
19
+
20
+ ## Acceptance Criteria
21
+ - [x] Current repo state has been inspected before implementation starts.
22
+ - [x] The delivered change is represented in Delano runtime assets, project contracts, validation, fixtures, or docs as appropriate.
23
+ - [x] The change is validated with the smallest meaningful command or fixture.
24
+ - [x] Evidence is recorded in this task or a task update before the task is marked done.
25
+
26
+ ## Evidence Log
27
+ - 2026-04-29T00:00:00Z: Task created; implementation evidence pending.
@@ -0,0 +1,27 @@
1
+ ---
2
+ id: T-001
3
+ name: Path leak fixture
4
+ status: done
5
+ workstream: WS-A
6
+ created: 2026-04-29T00:00:00Z
7
+ updated: 2026-04-29T00:10:00Z
8
+ linear_issue_id:
9
+ github_issue:
10
+ github_pr:
11
+ depends_on: []
12
+ conflicts_with: []
13
+ parallel: false
14
+ priority: high
15
+ estimate: S
16
+ ---
17
+
18
+ # Task: Path leak fixture
19
+
20
+ Leaked path: PATH_LEAK_TOKEN(home::example::private::delano)
21
+ WSL leak form: PATH_LEAK_TOKEN(wsl-mount::example::private::delano)
22
+
23
+ ## Acceptance Criteria
24
+ - [x] Current repo state has been inspected before implementation starts.
25
+
26
+ ## Evidence Log
27
+ - 2026-04-29T00:10:00Z: Inspected fixture and validation passed.
@@ -0,0 +1,9 @@
1
+ ---
2
+ created: 2026-04-01T00:00:00Z
3
+ updated: 2026-04-01T00:00:00Z
4
+ review_by: 2026-04-15T00:00:00Z
5
+ ---
6
+
7
+ # Stale context fixture
8
+
9
+ This fixture is intentionally past its review date.
@@ -0,0 +1,11 @@
1
+ {
2
+ "schema_version": 1,
3
+ "fixtures": [
4
+ { "name": "minimal-project", "kind": "valid", "path": "valid/minimal-project", "expected": "pass" },
5
+ { "name": "missing-evidence", "kind": "invalid", "path": "invalid/missing-evidence", "expected_rule": "missing-evidence" },
6
+ { "name": "broken-dependencies", "kind": "invalid", "path": "invalid/broken-dependencies", "expected_rule": "broken-dependencies" },
7
+ { "name": "stale-context", "kind": "invalid", "path": "invalid/stale-context", "expected_rule": "stale-context" },
8
+ { "name": "path-leak", "kind": "invalid", "path": "invalid/path-leak", "expected_rule": "path-leak" },
9
+ { "name": "invalid-transition", "kind": "invalid", "path": "invalid/invalid-transition", "expected_rule": "invalid-transition" }
10
+ ]
11
+ }
@@ -0,0 +1,27 @@
1
+ ---
2
+ id: T-001
3
+ name: Valid strict fixture task
4
+ status: done
5
+ workstream: WS-A
6
+ created: 2026-04-29T00:00:00Z
7
+ updated: 2026-04-29T00:10:00Z
8
+ linear_issue_id:
9
+ github_issue:
10
+ github_pr:
11
+ depends_on: []
12
+ conflicts_with: []
13
+ parallel: false
14
+ priority: high
15
+ estimate: S
16
+ ---
17
+
18
+ # Task: Valid strict fixture task
19
+
20
+ ## Acceptance Criteria
21
+ - [x] Current repo state has been inspected before implementation starts.
22
+ - [x] The delivered change is represented in Delano runtime assets, project contracts, validation, fixtures, or docs as appropriate.
23
+ - [x] The change is validated with the smallest meaningful command or fixture.
24
+ - [x] Evidence is recorded in this task or a task update before the task is marked done.
25
+
26
+ ## Evidence Log
27
+ - 2026-04-29T00:10:00Z: Inspected fixture project, added represented runtime fixture content, and validation passed: fixture validator smoke.
@@ -0,0 +1,19 @@
1
+ # Delano Read-Only Viewer
2
+
3
+ Minimal local frontend for browsing `.project` markdown contracts.
4
+
5
+ - Read-only: serves files from `.project` and does not write delivery state.
6
+ - Default URL: `http://127.0.0.1:3977`
7
+ - Override port: `DELANO_VIEWER_PORT=3987 npm run viewer`
8
+
9
+ Run from the repository root:
10
+
11
+ ```bash
12
+ npm run viewer
13
+ ```
14
+
15
+ The viewer indexes `.project/context/**/*.md`, `.project/templates/**/*.md`, and `.project/projects/**/*.md`. It derives artifact roles (`spec`, `plan`, `workstream`, `task`, `progress`, `decision`, `context`, `template`), status fields, task/workstream relationships, relationship-like wikilinks, snippets, and renders markdown in a Tolaria-inspired read-only pane layout.
16
+
17
+ Project folders get a right-side outline for the spec, plan, decisions/progress, workstreams, and tasks. Selecting a workstream focuses the list on that workstream and its subtasks. Context and template folders keep filters scoped to the roles/statuses that actually exist in the selected folder.
18
+
19
+ The reader stays read-only, but includes convenience buttons to open the selected markdown file's containing folder in the system explorer or open the file in VS Code. These actions are guarded so they only target markdown files inside `.project`.