Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ npm run build
node dist/cli/se-trace.js inspect path/to/cots.json
```

## 15-minute quickstart

Zero to a validated COTS in one command (Advancement Plan Phase 5 adopter quickstart):

```bash
bash scripts/quickstart.sh
```

It fetches (or falls back to the bundled) seed COTS, runs the quick conformance check, prints the inspect/cite summaries, and renders the COTS Explorer page. See `source-engineering-education/quickstart-15-minutes.md` for the guided version.

## Commands

```bash
Expand Down
15 changes: 15 additions & 0 deletions dist/audit.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Audit trail replay: print a decision's full lifecycle from a COTS's
* audit_history, marking append-only and linkage violations (RFC-0005,
* TC-L4-01..04).
*/
import type { Cots } from "./cots.js";
export interface AuditIssue {
test_id: string;
message: string;
}
export interface AuditReport {
lines: string[];
issues: AuditIssue[];
}
export declare const replayAudit: (cots: Cots, decisionId?: string) => AuditReport;
64 changes: 64 additions & 0 deletions dist/audit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Audit trail replay: print a decision's full lifecycle from a COTS's
* audit_history, marking append-only and linkage violations (RFC-0005,
* TC-L4-01..04).
*/
export const replayAudit = (cots, decisionId) => {
const lines = [];
const issues = [];
const decisions = decisionId ? cots.decisions.filter((d) => d.id === decisionId) : cots.decisions;
if (decisionId && decisions.length === 0) {
lines.push(`Decision not found: ${decisionId}`);
return { lines, issues };
}
const decisionIds = new Set(cots.decisions.map((d) => d.id));
for (const d of decisions) {
lines.push(`Decision: ${d.id}`);
lines.push(` status: ${d.validation_status.status} text: ${truncate(d.decision_text, 60)}`);
const history = [...d.audit_history].sort((a, b) => Date.parse(a.performed_at) - Date.parse(b.performed_at));
if (history.length === 0) {
issues.push({ test_id: "TC-L4-01", message: `${d.id}: empty audit_history` });
lines.push(" (no audit entries)");
continue;
}
if (history[0].action !== "created") {
issues.push({ test_id: "TC-L4-01", message: `${d.id}: audit trail does not start with "created"` });
}
const seen = new Set();
for (let j = 0; j < history.length; j++) {
const e = history[j];
lines.push(` [${e.performed_at}] ${e.action.toUpperCase()} by ${e.performed_by}`);
lines.push(` ${truncate(e.notes, 100)}`);
if (seen.has(e.id)) {
issues.push({ test_id: "TC-L4-02", message: `${d.id}: duplicate audit entry id '${e.id}' (append-only violated)` });
}
seen.add(e.id);
if (j > 0 && Date.parse(e.performed_at) < Date.parse(history[j - 1].performed_at)) {
issues.push({ test_id: "TC-L4-02", message: `${d.id}: audit entry out of time order (append-only violated)` });
}
if (e.action !== "created") {
const m = e.notes.match(/decision_id=(\S+)/);
if (!m || !decisionIds.has(m[1])) {
issues.push({ test_id: "TC-L4-03", message: `${d.id}: ${e.action} event does not resolve to a known decision` });
}
}
if (e.action === "rerun" && !/rerun_of=\S+/.test(e.notes)) {
issues.push({ test_id: "TC-L4-04", message: `${d.id}: rerun event lacks rerun_of linkage` });
}
if (e.action === "superseded" && !/superseded_by=\S+/.test(e.notes)) {
issues.push({ test_id: "TC-L4-04", message: `${d.id}: superseded event lacks superseded_by linkage` });
}
}
lines.push("");
}
if (issues.length === 0) {
lines.push("Audit trail: OK (append-only, lifecycle events resolve, linkages present)");
}
else {
lines.push(`Audit trail issues: ${issues.length}`);
for (const i of issues)
lines.push(` [${i.test_id}] ${i.message}`);
}
return { lines, issues };
};
const truncate = (s, n) => s.length > n ? s.slice(0, n) + "..." : s;
5 changes: 5 additions & 0 deletions dist/cite.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Cite: print the evidence chain for a specific decision in a COTS.
*/
import type { Cots } from "./cots.js";
export declare const cite: (cots: Cots, decisionId: string) => string;
56 changes: 56 additions & 0 deletions dist/cite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Cite: print the evidence chain for a specific decision in a COTS.
*/
export const cite = (cots, decisionId) => {
const decision = cots.decisions.find((d) => d.id === decisionId);
if (!decision) {
return `Decision ${decisionId} not found in COTS ${cots.cots_id}.`;
}
const chain = cots.evidence.evidence_chain.find((c) => c.decision_id === decisionId);
if (!chain) {
return `Decision ${decisionId} has no evidence chain.`;
}
const lines = [];
lines.push(`Decision: ${decision.decision_text}`);
lines.push("");
for (const claim of chain.claims) {
lines.push(`Claim: ${claim.claim_text}`);
lines.push(` type: ${claim.claim_type}`);
lines.push("");
if (claim.supporting_evidence.length > 0) {
lines.push(" Supporting evidence:");
for (const id of claim.supporting_evidence) {
const item = chain.supporting_evidence.find((e) => e.id === id);
if (item) {
const source = cots.inputs.source_artifacts.find((a) => a.id === item.source_id);
const sourceLabel = source
? `${source.id} (${source.content_reference?.uri ?? "no uri"})`
: item.source_id;
lines.push(` - ${item.id} <- ${sourceLabel}`);
lines.push(` op: ${item.transformation.operation}`);
lines.push(` input: ${item.transformation.inputs.join(", ")}`);
lines.push(` output: ${item.transformation.output.slice(0, 200)}${item.transformation.output.length > 200 ? "..." : ""}`);
lines.push(` conf: ${item.uncertainty.calibrated_score}`);
lines.push(` basis: ${item.uncertainty.basis}`);
}
}
}
if (claim.contradicting_evidence.length > 0) {
lines.push(" Contradicting evidence:");
for (const id of claim.contradicting_evidence) {
const item = chain.contradicting_evidence.find((e) => e.id === id);
if (item) {
const source = cots.inputs.source_artifacts.find((a) => a.id === item.source_id);
const sourceLabel = source
? `${source.id} (${source.content_reference?.uri ?? "no uri"})`
: item.source_id;
lines.push(` - ${item.id} <- ${sourceLabel}`);
lines.push(` op: ${item.transformation.operation}`);
lines.push(` conf: ${item.uncertainty.calibrated_score}`);
lines.push(` basis: ${item.uncertainty.basis}`);
}
}
}
}
return lines.join("\n");
};
13 changes: 13 additions & 0 deletions dist/cli/se-trace.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* The se-trace CLI. Three subcommands:
* - validate <file> Run the conformance suite against a COTS.
* - validate-runtime <file> Surface the Layer 2 ValidationFlags of a COTS.
* - inspect <file> Print a human-readable summary of a COTS.
* - cite <file> <decision-id> Print the evidence chain for a specific decision.
* - visualize <file> Render a COTS as a Reasoning Roadmap (Mermaid + HTML).
* - context-graph <file> Render a COTS's Context + Evidence as a Context Graph (RFC-0004).
* - audit <file> [decision-id] Replay a decision's audit trail (RFC-0005).
* - explore <file> Render the COTS Explorer: one self-contained HTML page
* (Reasoning Roadmap + Context Graph + summary + audit trail).
*/
export {};
Loading
Loading