@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.
- package/.delano/README.md +7 -0
- package/.delano/viewer/README.md +19 -0
- package/.delano/viewer/public/app.js +818 -0
- package/.delano/viewer/public/explorer.svg +3 -0
- package/.delano/viewer/public/index.html +21 -0
- package/.delano/viewer/public/markdown.svg +6 -0
- package/.delano/viewer/public/styles.css +1042 -0
- package/.delano/viewer/public/vscode.svg +24 -0
- package/.delano/viewer/server.js +389 -0
- package/HANDBOOK.md +65 -45
- package/README.md +10 -2
- package/assets/install-manifest.json +112 -35
- package/assets/payload/.agents/README.md +31 -6
- package/assets/payload/.agents/adapters/claude/README.md +22 -3
- package/assets/payload/.agents/adapters/codex/README.md +22 -3
- package/assets/payload/.agents/adapters/opencode/README.md +22 -3
- package/assets/payload/.agents/adapters/pi/README.md +22 -3
- package/assets/payload/.agents/common/log-safety.js +55 -0
- package/assets/payload/.agents/eval-fixtures/skill-output/invalid/missing-evidence/output.json +6 -0
- package/assets/payload/.agents/eval-fixtures/skill-output/valid/summary/output.json +7 -0
- package/assets/payload/.agents/fixtures/github/status-snapshot.json +6 -0
- package/assets/payload/.agents/fixtures/linear/issue-snapshot.json +6 -0
- package/assets/payload/.agents/hooks/bash-worktree-fix.sh +2 -1
- package/assets/payload/.agents/hooks/post-tool-logger.js +2 -1
- package/assets/payload/.agents/hooks/user-prompt-logger.js +17 -1
- package/assets/payload/.agents/logs/delivery-metrics.md +22 -0
- package/assets/payload/.agents/logs/schema.md +20 -1
- package/assets/payload/.agents/rules/delivery-modes.md +17 -0
- package/assets/payload/.agents/schemas/README.md +22 -0
- package/assets/payload/.agents/schemas/artifact-scope.json +237 -0
- package/assets/payload/.agents/schemas/artifacts/context.schema.json +11 -0
- package/assets/payload/.agents/schemas/artifacts/decision_log.schema.json +12 -0
- package/assets/payload/.agents/schemas/artifacts/evidence.schema.json +17 -0
- package/assets/payload/.agents/schemas/artifacts/plan.schema.json +83 -0
- package/assets/payload/.agents/schemas/artifacts/spec.schema.json +101 -0
- package/assets/payload/.agents/schemas/artifacts/task.schema.json +121 -0
- package/assets/payload/.agents/schemas/artifacts/update.schema.json +12 -0
- package/assets/payload/.agents/schemas/artifacts/workstream.schema.json +66 -0
- package/assets/payload/.agents/schemas/evidence-map.json +53 -0
- package/assets/payload/.agents/schemas/learning/closeout-learning-proposal.schema.json +20 -0
- package/assets/payload/.agents/schemas/learning/delivery-metric-event.schema.json +21 -0
- package/assets/payload/.agents/schemas/leases/lease.schema.json +39 -0
- package/assets/payload/.agents/schemas/metrics/delivery-event.schema.json +29 -0
- package/assets/payload/.agents/schemas/metrics/delivery-events.schema.json +49 -0
- package/assets/payload/.agents/schemas/operating-modes.json +42 -0
- package/assets/payload/.agents/schemas/status-transitions.json +31 -0
- package/assets/payload/.agents/schemas/sync/drift-report.schema.json +25 -0
- package/assets/payload/.agents/schemas/sync/drift-taxonomy.json +38 -0
- package/assets/payload/.agents/schemas/sync/sync-map.schema.json +39 -0
- package/assets/payload/.agents/scripts/README.md +1 -0
- package/assets/payload/.agents/scripts/audit-context-files.mjs +54 -0
- package/assets/payload/.agents/scripts/audit-context-scoring.mjs +14 -0
- package/assets/payload/.agents/scripts/build-drift-report.mjs +133 -0
- package/assets/payload/.agents/scripts/check-artifact-schemas.mjs +116 -0
- package/assets/payload/.agents/scripts/check-closeout-learning-proposals.mjs +23 -0
- package/assets/payload/.agents/scripts/check-context-audit.mjs +61 -0
- package/assets/payload/.agents/scripts/check-delivery-metric-events.mjs +35 -0
- package/assets/payload/.agents/scripts/check-delivery-metrics.mjs +52 -0
- package/assets/payload/.agents/scripts/check-evidence-map.mjs +143 -0
- package/assets/payload/.agents/scripts/check-github-status-inspection.mjs +93 -0
- package/assets/payload/.agents/scripts/check-github-sync.mjs +159 -0
- package/assets/payload/.agents/scripts/check-handoff-summaries.mjs +57 -0
- package/assets/payload/.agents/scripts/check-lease-conflicts.mjs +24 -0
- package/assets/payload/.agents/scripts/check-lease-contracts.mjs +17 -0
- package/assets/payload/.agents/scripts/check-linear-issue-inspection.mjs +63 -0
- package/assets/payload/.agents/scripts/check-local-sync-map.mjs +151 -0
- package/assets/payload/.agents/scripts/check-log-safety.sh +62 -0
- package/assets/payload/.agents/scripts/check-operating-modes.mjs +99 -0
- package/assets/payload/.agents/scripts/check-path-standards.sh +1 -1
- package/assets/payload/.agents/scripts/check-skill-output-evals.mjs +13 -0
- package/assets/payload/.agents/scripts/check-status-transitions.mjs +169 -0
- package/assets/payload/.agents/scripts/check-strict-fixtures.mjs +140 -0
- package/assets/payload/.agents/scripts/check-sync-schemas.mjs +52 -0
- package/assets/payload/.agents/scripts/check-text-safety.mjs +158 -0
- package/assets/payload/.agents/scripts/check-worktree-health.mjs +100 -0
- package/assets/payload/.agents/scripts/fix-path-standards.sh +1 -1
- package/assets/payload/.agents/scripts/inspect-github-sync.mjs +108 -0
- package/assets/payload/.agents/scripts/lease-manager.mjs +88 -0
- package/assets/payload/.agents/scripts/log-event.js +3 -0
- package/assets/payload/.agents/scripts/plan-sync-repairs.mjs +66 -0
- package/assets/payload/.agents/scripts/pm/validate.sh +656 -2
- package/assets/payload/.agents/scripts/propose-closeout-learning.mjs +20 -0
- package/assets/payload/.agents/scripts/read-local-sync-map.mjs +135 -0
- package/assets/payload/.agents/scripts/select-next-task.mjs +22 -0
- package/assets/payload/.agents/scripts/summarize-project-metrics.mjs +15 -0
- package/assets/payload/.agents/skills/closeout-skill/SKILL.md +3 -0
- package/assets/payload/.agents/skills/closeout-skill/references/runbook.md +5 -2
- package/assets/payload/.agents/skills/closeout-skill/templates/closure-checklist.md +2 -0
- package/assets/payload/.agents/skills/closeout-skill/templates/learning-proposal.md +21 -0
- package/assets/payload/.agents/skills/closeout-skill/templates/learning-proposals.md +25 -0
- package/assets/payload/.agents/validation-fixtures/strict/invalid/broken-dependencies/dependency.md +18 -0
- package/assets/payload/.agents/validation-fixtures/strict/invalid/broken-dependencies/task.md +24 -0
- package/assets/payload/.agents/validation-fixtures/strict/invalid/invalid-transition/task.md +20 -0
- package/assets/payload/.agents/validation-fixtures/strict/invalid/missing-evidence/task.md +27 -0
- package/assets/payload/.agents/validation-fixtures/strict/invalid/path-leak/task.md +27 -0
- package/assets/payload/.agents/validation-fixtures/strict/invalid/stale-context/context.md +9 -0
- package/assets/payload/.agents/validation-fixtures/strict/manifest.json +11 -0
- package/assets/payload/.agents/validation-fixtures/strict/valid/minimal-project/task.md +27 -0
- package/assets/payload/.delano/viewer/README.md +19 -0
- package/assets/payload/.delano/viewer/public/app.js +818 -0
- package/assets/payload/.delano/viewer/public/explorer.svg +3 -0
- package/assets/payload/.delano/viewer/public/index.html +21 -0
- package/assets/payload/.delano/viewer/public/markdown.svg +6 -0
- package/assets/payload/.delano/viewer/public/styles.css +1042 -0
- package/assets/payload/.delano/viewer/public/vscode.svg +24 -0
- package/assets/payload/.delano/viewer/server.js +389 -0
- package/assets/payload/.project/templates/plan.md +1 -1
- package/assets/payload/.project/templates/spec.md +1 -1
- package/assets/payload/.project/templates/task.md +1 -0
- package/assets/payload/HANDBOOK.md +65 -45
- package/package.json +31 -2
- package/src/cli/commands/viewer.js +81 -0
- 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.
|
|
7
|
-
5.
|
|
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
|
|
@@ -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.
|
package/assets/payload/.agents/validation-fixtures/strict/invalid/broken-dependencies/dependency.md
ADDED
|
@@ -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,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`.
|