diff --git a/.eleventy.js b/.eleventy.js
index 2fcb4d2..f019fb1 100644
--- a/.eleventy.js
+++ b/.eleventy.js
@@ -14,6 +14,12 @@ import { installMathRenderer } from './scripts/markdown-math.mjs';
// then: the same source bytes are copied to the public data route without runtime fetching or schema shadowing
// class: evidence
// since: 2026-08-03
+//
+// id: website_sitrep_has_one_public_projection_copy
+// given: repo-owned plan reports are validated and projected through the pinned skill-lib work-graph instrument
+// then: the website publishes the generated presentation view without becoming a second source of repo canon
+// class: evidence
+// since: 2026-08-14
// === END CONTRACTS ===
// Usage: run `npm run build`; Eleventy emits the static site, copies root machine instructions, and preserves dependency-free public reading paths.
@@ -23,6 +29,7 @@ export default function configureEleventy(eleventyConfig) {
eleventyConfig.addPassthroughCopy({
'src/assets': 'assets',
'src/_data/gonol_relationship_display.json': 'assets/data/gonol-relationship-display-v1.json',
+ 'src/_data/generated/sitrep.json': 'assets/data/sitrep.json',
'CNAME': 'CNAME',
'llms.txt': 'llms.txt',
'artifacts/four-cuts-1.html': 'artifacts/four-cuts/index.html',
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index d3f1349..9e3eb9b 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -7,7 +7,7 @@ on:
repository_dispatch:
types: [org-repo-updated]
schedule:
- - cron: '24 5 * * *'
+ - cron: '24 * * * *'
permissions:
contents: read
diff --git a/docs/work-graphs/repository-plan-report.json b/docs/work-graphs/repository-plan-report.json
new file mode 100644
index 0000000..3b177ae
--- /dev/null
+++ b/docs/work-graphs/repository-plan-report.json
@@ -0,0 +1,157 @@
+{
+ "schema": "the-interdependency.repository-plan-report",
+ "version": "1.0.0",
+ "repository": "The-Interdependency/The-Interdependency.github.io",
+ "contract": {
+ "repository": "The-Interdependency/skill-lib",
+ "path": "interdependent-work-graph/repository-plan-report.schema.json",
+ "version": "1.0.0",
+ "blob_sha": "9b347b2dff7692054b571602f30ee6d00c2e7265"
+ },
+ "source": {
+ "branch": "feature/dynamic-repo-sitrep",
+ "commit": "8f41144653fd084e4f0ef433367ee01faee9d29e",
+ "generated_at": "2026-08-14",
+ "note": "This report describes the SITREP implementation commit immediately beneath the first website self-report. The report commit is coordination metadata and does not acquire semantic, mathematical, measurement, runtime, empirical, or publication authority from the repositories it presents."
+ },
+ "authority": {
+ "owns": [
+ "public website presentation, navigation, static build behavior, and publication surfaces for interdependentway.org",
+ "website-local data fetching, caching, fallback behavior, accessibility, and presentation contracts",
+ "public rendering of repository-owned reports and GitHub telemetry without altering their source authority",
+ "the website-specific SITREP presentation view and its machine-readable public copy"
+ ],
+ "does_not_own": [
+ "METAPAT semantic canon or ontology validity",
+ "UCNS mathematical representation, geometry, or proof status",
+ "EDCM measurement validity, empirical results, or experiment authority",
+ "a0 runtime, agent identity, memory, privacy, or deployment authority",
+ "skill-lib reusable skill doctrine, repository-plan-report contract, or portfolio projection semantics"
+ ],
+ "non_transfer": [
+ "publication on this website does not transfer producer-repository authority to the website",
+ "GitHub workflow success does not establish semantic, mathematical, measurement, empirical, or runtime validity",
+ "the SITREP presentation view does not become a second source of repository canon",
+ "a stale or missing repository report remains visibly incomplete rather than being reconstructed from telemetry or memory",
+ "skill-lib validation and deterministic projection constrain coordination representation but do not decide substantive project truth"
+ ]
+ },
+ "portfolio_role": {
+ "summary": "Provide the public presentation and observability surface for repo-owned situation reports: consume the exact skill-lib work-graph contract and projection, overlay separately observed GitHub activity, and make current relations, blockers, next actions, and unresolved boundaries legible without centralizing repository canon.",
+ "upstream_contract": "Participating repositories own the claims in their repository-plan-report.json files; skill-lib owns the frozen report contract and deterministic projection instrument.",
+ "downstream_contract": "The website exposes human-readable infograms and a machine-readable generated SITREP view while preserving source identities, fallback state, and authority-transfer boundaries.",
+ "reports_to": {
+ "repository": "The-Interdependency/skill-lib",
+ "skill": "interdependent-work-graph",
+ "relation": "repo-owned publication and observability status input; no authority transfer"
+ }
+ },
+ "status": {
+ "state": "implemented static public knowledge system with dynamic repository SITREP pending branch validation and deployment",
+ "current_claim": "The website now contains a build-time SITREP path for METAPAT, UCNS, EDCM, a0, and itself that executes the exact pinned skill-lib portfolio projection, preserves missing reports and fallback explicitly, and renders GitHub telemetry separately from repo-declared situation; public deployment evidence remains pending merge.",
+ "latest_main_milestone": {
+ "commit": "4d4da2436ae556e5274cf5cb98f601e34bb793d3",
+ "description": "current main before the dynamic SITREP branch: static-first public knowledge system with provenance subordinated to human reading"
+ }
+ },
+ "delivered": [
+ {
+ "surface": "static-first public knowledge system",
+ "status": "implemented and deployed on current main",
+ "boundary": "publication and rendering do not establish the validity of producer-repository claims"
+ },
+ {
+ "surface": "generated organization project map",
+ "status": "implemented build-time GitHub observation and reviewed project-manifest presentation",
+ "boundary": "project discovery and GitHub metadata do not constitute portfolio membership or repository-plan status"
+ },
+ {
+ "surface": "skill-lib-governed repository SITREP collector",
+ "status": "implemented on feature branch pending executable CI evidence",
+ "boundary": "collector executes a commit- and blob-pinned skill-lib portfolio_plan.py rather than reimplementing its projection semantics"
+ },
+ {
+ "surface": "repository SITREP infogram page",
+ "status": "implemented on feature branch with declared-versus-observed separation and explicit fallback",
+ "boundary": "presentation does not reconcile report/HEAD mismatches or upgrade source claims"
+ },
+ {
+ "surface": "machine-readable public SITREP view",
+ "status": "configured for build-time publication at assets/data/sitrep.json",
+ "boundary": "generated JSON is a presentation projection and not a replacement for repo-owned reports"
+ }
+ ],
+ "active_frontier": [
+ "validate the SITREP collector, skill-lib projection execution, Eleventy rendering, links, and accessibility in CI",
+ "merge and deploy the SITREP page only after existing website release gates pass",
+ "refresh the website repo report after merge while preserving the report-refresh commit as coordination metadata",
+ "decide whether producer repositories should emit repository_dispatch events for near-immediate refresh in addition to the hourly fallback schedule",
+ "decide which additional repositories, if any, intentionally join the public SITREP portfolio after the initial five-surface scope"
+ ],
+ "next_actions": [
+ {
+ "action": "run the full pull-request validation suite against the dynamic SITREP branch",
+ "owner": "The-Interdependency/The-Interdependency.github.io",
+ "dependency": "GitHub Actions runner and public GitHub API access"
+ },
+ {
+ "action": "merge and deploy the SITREP only if build, unit, generated-site, browser, accessibility, and link gates remain green",
+ "owner": "The-Interdependency/The-Interdependency.github.io",
+ "dependency": "successful pull-request validation"
+ },
+ {
+ "action": "add producer-side org-repo-updated dispatch only where repository permissions and failure behavior can be kept explicit",
+ "owner": "participating repositories",
+ "dependency": "post-deployment decision; hourly scheduled refresh already provides bounded eventual update"
+ }
+ ],
+ "blocked": [
+ "public SITREP deployment is blocked on branch validation and merge"
+ ],
+ "cross_repository_relations": [
+ {
+ "repository": "The-Interdependency/skill-lib",
+ "relation": "report-contract and deterministic work-graph projection authority consumed by the website",
+ "authority_transfer": false
+ },
+ {
+ "repository": "The-Interdependency/metapat",
+ "relation": "repo-owned semantic situation report presented without semantic authority transfer",
+ "authority_transfer": false
+ },
+ {
+ "repository": "The-Interdependency/ucns",
+ "relation": "repo-owned mathematical and representation situation report presented without proof-status transfer",
+ "authority_transfer": false,
+ "proof_status_transfer": false
+ },
+ {
+ "repository": "The-Interdependency/edcm",
+ "relation": "repo-owned measurement situation report presented without measurement-status transfer",
+ "authority_transfer": false,
+ "measurement_status_transfer": false
+ },
+ {
+ "repository": "The-Interdependency/a0",
+ "relation": "repo-owned runtime situation report presented without runtime or deployment authority transfer",
+ "authority_transfer": false
+ }
+ ],
+ "machine_entrypoints": {
+ "repo_report": "docs/work-graphs/repository-plan-report.json",
+ "sitrep_collector": "scripts/fetch-sitrep.mjs",
+ "sitrep_page": "src/sitrep/index.njk",
+ "sitrep_styles": "src/assets/css/sitrep.css",
+ "public_sitrep_data": "assets/data/sitrep.json",
+ "project_manifest": ".interdependency/project.yml",
+ "site_configuration": ".eleventy.js",
+ "release_workflow": ".github/workflows/pages.yml"
+ },
+ "hmmm": [
+ "producer-side repository_dispatch coverage for near-immediate SITREP rebuilds",
+ "whether the public portfolio remains exactly METAPAT, UCNS, EDCM, a0, and website or later admits additional intentional members",
+ "long-term historical SITREP snapshot retention and public timeline policy",
+ "cryptographic producer authentication beyond GitHub transport and content identities",
+ "whether report freshness should later distinguish substantive repository change from report-only coordination commits"
+ ]
+}
diff --git a/package.json b/package.json
index 75f1e78..5b3775b 100644
--- a/package.json
+++ b/package.json
@@ -5,9 +5,10 @@
"type": "module",
"scripts": {
"dev": "npm run refresh:data && eleventy --serve",
- "refresh:data": "npm run refresh:canon && npm run refresh:github && npm run refresh:textbook && npm run refresh:works",
+ "refresh:data": "npm run refresh:canon && npm run refresh:github && npm run refresh:sitrep && npm run refresh:textbook && npm run refresh:works",
"refresh:canon": "node scripts/fetch-canon.mjs && node scripts/parse-canon.mjs",
"refresh:github": "node scripts/fetch-github-org.mjs",
+ "refresh:sitrep": "node scripts/fetch-sitrep.mjs",
"refresh:textbook": "node scripts/fetch-textbook.mjs",
"research:enrich": "node scripts/enrich-citations.mjs",
"audit:workflows": "node scripts/audit-workflows.mjs",
@@ -19,7 +20,7 @@
"validate": "node scripts/validate-content.mjs && node scripts/verify-generated-routes.mjs && node scripts/verify-article-canon.mjs",
"build": "npm run validate && eleventy && pagefind --site _site && node scripts/write-build-info.mjs",
"pretest": "node scripts/prepare-tests.mjs",
- "test": "node --test tests/aicontext.test.mjs tests/edcm-mathematics.test.mjs tests/gonol-relationships.test.mjs tests/post-merge-reconciliation.test.mjs tests/llms-build.test.mjs tests/canon-parser.test.mjs tests/canon-integrity.test.mjs tests/textbook-integrity.test.mjs tests/math-rendering.test.mjs tests/narratives.test.mjs tests/offline-project-snapshot.test.mjs tests/repo-coverage.test.mjs tests/research-ledger.test.mjs tests/works-registry.test.mjs tests/site-contract.test.mjs",
+ "test": "node --test tests/aicontext.test.mjs tests/edcm-mathematics.test.mjs tests/gonol-relationships.test.mjs tests/post-merge-reconciliation.test.mjs tests/llms-build.test.mjs tests/canon-parser.test.mjs tests/canon-integrity.test.mjs tests/textbook-integrity.test.mjs tests/math-rendering.test.mjs tests/narratives.test.mjs tests/offline-project-snapshot.test.mjs tests/repo-coverage.test.mjs tests/research-ledger.test.mjs tests/works-registry.test.mjs tests/site-contract.test.mjs tests/sitrep.test.mjs",
"test:generated": "node --test tests/generated-site.test.mjs tests/human-ui-generated.test.mjs tests/textbook-generated.test.mjs tests/math-generated.test.mjs && node tests/links.test.mjs",
"test:browser": "playwright test",
"test:e2e": "playwright test tests/site.spec.mjs",
diff --git a/scripts/fetch-sitrep.mjs b/scripts/fetch-sitrep.mjs
new file mode 100644
index 0000000..2a91f33
--- /dev/null
+++ b/scripts/fetch-sitrep.mjs
@@ -0,0 +1,293 @@
+import { execFileSync } from 'node:child_process';
+import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import path from 'node:path';
+
+// === MODULE_BUILD ===
+// id: repository_sitrep_projection
+// purpose: Build the public repository SITREP from repo-owned plan reports through the exact skill-lib portfolio projection, then add separate GitHub telemetry.
+// entrypoint: npm run refresh:sitrep
+// tests: tests/sitrep.test.mjs
+// === END MODULE_BUILD ===
+// === BOUNDARIES ===
+// id: sitrep_authority_boundary
+// network: reads public GitHub repository metadata and repo-owned reports; optional token raises rate limits
+// storage: writes generated and last-known-good SITREP JSON only
+// authority: skill-lib owns the reporting contract and deterministic projection; each repository owns its report claims; this site owns presentation only
+// failure: never reconstructs missing reports; never publishes raw command errors or credentials; missing or HEAD-different sources remain visible and a last-known-good projection may be used only with fallback=true
+// === END BOUNDARIES ===
+// Usage: run `npm run refresh:sitrep`; the output is presentation data, not a new source of repository canon.
+
+const org = 'The-Interdependency';
+const githubApiOrigin = 'https://api.github.com';
+const websiteRepository = `${org}/The-Interdependency.github.io`;
+const localReportPath = 'docs/work-graphs/repository-plan-report.json';
+
+// Exact control-plane identity. The report schema is already frozen by blob SHA;
+// the portfolio script is additionally pinned so the website cannot silently
+// reinterpret repo reports when skill-lib main changes.
+const controlPlane = {
+ repository: `${org}/skill-lib`,
+ commit: '6ef2e4c123225f9db20e5230e5894c9c86b42ee6',
+ skill: 'interdependent-work-graph',
+ reportSchemaVersion: '1.0.0',
+ reportSchemaPath: 'interdependent-work-graph/repository-plan-report.schema.json',
+ reportSchemaBlob: '9b347b2dff7692054b571602f30ee6d00c2e7265',
+ portfolioScriptPath: 'interdependent-work-graph/portfolio_plan.py',
+ portfolioScriptBlob: '97b8b546b4151486164c8a4b730c24a8c895b25b'
+};
+
+const projectDefinitions = [
+ { repository: `${org}/metapat`, name: 'metapat', label: 'METAPAT', slug: 'metapat' },
+ { repository: `${org}/ucns`, name: 'ucns', label: 'UCNS', slug: 'ucns' },
+ { repository: `${org}/edcm`, name: 'edcm', label: 'EDCM', slug: 'edcm' },
+ { repository: `${org}/a0`, name: 'a0', label: 'a0', slug: 'a0' },
+ { repository: websiteRepository, name: 'The-Interdependency.github.io', label: 'Website', slug: 'website', localReport: true }
+];
+
+const headers = ['-H', 'Accept: application/vnd.github+json', '-H', 'X-GitHub-Api-Version: 2022-11-28'];
+if (process.env.GITHUB_TOKEN) headers.push('-H', `Authorization: Bearer ${process.env.GITHUB_TOKEN}`);
+
+function githubApiUrl(pathname, search = {}) {
+ const url = new URL(pathname, githubApiOrigin);
+ for (const [key, value] of Object.entries(search)) url.searchParams.set(key, String(value));
+ return url;
+}
+
+function getJson(target) {
+ const url = target instanceof URL ? target : new URL(target);
+ if (url.protocol !== 'https:' || url.origin !== githubApiOrigin) {
+ throw new Error(`refusing non-GitHub API target: ${url.origin}`);
+ }
+ return JSON.parse(execFileSync(
+ 'curl',
+ ['-fsSL', '--retry', '2', '--max-time', '30', ...headers, url.href],
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }
+ ));
+}
+
+function tryGetJson(target) {
+ try { return getJson(target); } catch { return null; }
+}
+
+function publicFailureReason(error, scope = 'refresh') {
+ const message = String(error?.message || error || '');
+ if (message.includes('offline requested')) return 'offline refresh requested';
+ if (message.includes('skill-lib report schema drift')) return 'skill-lib report schema identity mismatch';
+ if (message.includes('skill-lib portfolio script drift')) return 'skill-lib portfolio projection identity mismatch';
+ if (message.includes('no repository plan reports could be collected')) return 'repository plan reports unavailable';
+ if (message.includes('repo report identifies') || message.includes('expected base64 GitHub contents response')) return `${scope} report invalid or unavailable`;
+ if (message.includes('Python interpreter') || message.includes('portfolio projection')) return 'skill-lib portfolio projection execution failed';
+ return `${scope} unavailable`;
+}
+
+function decodeContent(response, label) {
+ if (!response?.content || response.encoding !== 'base64') {
+ throw new Error(`${label}: expected base64 GitHub contents response`);
+ }
+ return Buffer.from(response.content, 'base64').toString('utf8');
+}
+
+function runPortfolio(scriptPath, reportPaths, outputPath) {
+ let lastError = null;
+ for (const executable of ['python3', 'python']) {
+ try {
+ execFileSync(executable, [scriptPath, ...reportPaths, '--output', outputPath], {
+ encoding: 'utf8',
+ stdio: ['ignore', 'pipe', 'pipe']
+ });
+ return executable;
+ } catch (error) {
+ lastError = error;
+ if (error?.code !== 'ENOENT') break;
+ }
+ }
+ throw lastError || new Error('no Python interpreter available for skill-lib portfolio projection');
+}
+
+async function collectReport(definition, workDir) {
+ let text;
+ let reportBlob = null;
+ if (definition.localReport) {
+ text = await readFile(localReportPath, 'utf8');
+ } else {
+ const response = getJson(githubApiUrl(
+ `/repos/${encodeURIComponent(org)}/${encodeURIComponent(definition.name)}/contents/docs/work-graphs/repository-plan-report.json`,
+ { ref: 'main' }
+ ));
+ reportBlob = response.sha || null;
+ text = decodeContent(response, definition.repository);
+ }
+ const report = JSON.parse(text);
+ if (report.repository !== definition.repository) {
+ throw new Error(`${definition.repository}: repo report identifies ${report.repository || 'hmmm'}`);
+ }
+ const reportPath = path.join(workDir, `${definition.slug}.repository-plan-report.json`);
+ await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`);
+ return { report, reportPath, reportBlob };
+}
+
+function collectTelemetry(definition) {
+ const repo = getJson(githubApiUrl(`/repos/${encodeURIComponent(org)}/${encodeURIComponent(definition.name)}`));
+ const branch = repo.default_branch || 'main';
+ const commit = getJson(githubApiUrl(`/repos/${encodeURIComponent(org)}/${encodeURIComponent(definition.name)}/commits/${encodeURIComponent(branch)}`));
+ const pulls = tryGetJson(githubApiUrl(`/repos/${encodeURIComponent(org)}/${encodeURIComponent(definition.name)}/pulls`, {
+ state: 'open',
+ per_page: 100
+ })) || [];
+ const runResponse = tryGetJson(githubApiUrl(`/repos/${encodeURIComponent(org)}/${encodeURIComponent(definition.name)}/actions/runs`, {
+ branch,
+ per_page: 1
+ }));
+ const latestRun = runResponse?.workflow_runs?.[0] || null;
+ return {
+ branch,
+ head: commit.sha || null,
+ headDate: commit.commit?.committer?.date || commit.commit?.author?.date || null,
+ pushedAt: repo.pushed_at || null,
+ updatedAt: repo.updated_at || null,
+ openPullRequests: Array.isArray(pulls) ? pulls.length : null,
+ latestWorkflow: latestRun ? {
+ name: latestRun.name || null,
+ status: latestRun.status || null,
+ conclusion: latestRun.conclusion || null,
+ event: latestRun.event || null,
+ headSha: latestRun.head_sha || null,
+ updatedAt: latestRun.updated_at || null,
+ url: latestRun.html_url || null
+ } : null
+ };
+}
+
+function projectView(definition, reportRecord, telemetry, portfolioView) {
+ const sourceCommit = reportRecord?.report?.source?.commit || null;
+ const head = telemetry?.head || null;
+ let reportFreshness = 'unknown';
+ if (sourceCommit && head) reportFreshness = sourceCommit === head ? 'current' : 'HEAD differs';
+ else if (!reportRecord) reportFreshness = 'missing';
+ return {
+ ...definition,
+ report: reportRecord?.report || null,
+ reportBlob: reportRecord?.reportBlob || null,
+ portfolio: portfolioView || null,
+ telemetry: telemetry || null,
+ reportFreshness
+ };
+}
+
+async function writeGenerated(data, persistSnapshot = false) {
+ await mkdir('src/_data/generated', { recursive: true });
+ await mkdir('src/_data/snapshots', { recursive: true });
+ await writeFile('src/_data/generated/sitrep.json', `${JSON.stringify(data, null, 2)}\n`);
+ if (persistSnapshot) {
+ await writeFile('src/_data/snapshots/sitrep.last-known-good.json', `${JSON.stringify(data, null, 2)}\n`);
+ }
+}
+
+async function fallbackData(reason) {
+ try {
+ const previous = JSON.parse(await readFile('src/_data/snapshots/sitrep.last-known-good.json', 'utf8'));
+ return {
+ ...previous,
+ snapshotAt: new Date().toISOString(),
+ fallback: true,
+ fallbackReason: reason,
+ sourceSnapshotAt: previous.snapshotAt || null
+ };
+ } catch {
+ return {
+ schema: 'the-interdependency.website-sitrep-view',
+ version: '1.0.0',
+ snapshotAt: new Date().toISOString(),
+ fallback: true,
+ fallbackReason: reason,
+ sourceSnapshotAt: null,
+ controlPlane,
+ portfolio: null,
+ missingReports: projectDefinitions.map(project => project.repository),
+ projects: projectDefinitions.map(project => ({
+ ...project,
+ report: null,
+ portfolio: null,
+ telemetry: null,
+ reportFreshness: 'unknown'
+ }))
+ };
+ }
+}
+
+let workDir = null;
+try {
+ if (process.env.OFFLINE === '1') throw new Error('offline requested');
+ workDir = await mkdtemp(path.join(tmpdir(), 'interdependency-sitrep-'));
+
+ const schemaResponse = getJson(githubApiUrl(
+ `/repos/${encodeURIComponent(org)}/skill-lib/contents/${controlPlane.reportSchemaPath}`,
+ { ref: controlPlane.commit }
+ ));
+ if (schemaResponse.sha !== controlPlane.reportSchemaBlob) {
+ throw new Error(`skill-lib report schema drift: expected ${controlPlane.reportSchemaBlob}, got ${schemaResponse.sha || 'hmmm'}`);
+ }
+
+ const scriptResponse = getJson(githubApiUrl(
+ `/repos/${encodeURIComponent(org)}/skill-lib/contents/${controlPlane.portfolioScriptPath}`,
+ { ref: controlPlane.commit }
+ ));
+ if (scriptResponse.sha !== controlPlane.portfolioScriptBlob) {
+ throw new Error(`skill-lib portfolio script drift: expected ${controlPlane.portfolioScriptBlob}, got ${scriptResponse.sha || 'hmmm'}`);
+ }
+ const portfolioScript = path.join(workDir, 'portfolio_plan.py');
+ await writeFile(portfolioScript, decodeContent(scriptResponse, 'skill-lib portfolio_plan.py'));
+
+ const reportRecords = new Map();
+ const missingReports = [];
+ for (const definition of projectDefinitions) {
+ try {
+ reportRecords.set(definition.repository, await collectReport(definition, workDir));
+ } catch (error) {
+ missingReports.push({ repository: definition.repository, reason: publicFailureReason(error, definition.repository) });
+ }
+ }
+ if (reportRecords.size === 0) throw new Error('no repository plan reports could be collected');
+
+ const portfolioOutput = path.join(workDir, 'portfolio-plan.json');
+ const python = runPortfolio(
+ portfolioScript,
+ [...reportRecords.values()].map(record => record.reportPath),
+ portfolioOutput
+ );
+ const portfolio = JSON.parse(await readFile(portfolioOutput, 'utf8'));
+
+ const portfolioByRepo = new Map((portfolio.repositories || []).map(item => [item.repository, item]));
+ const telemetry = new Map();
+ for (const definition of projectDefinitions) {
+ try { telemetry.set(definition.repository, collectTelemetry(definition)); }
+ catch { telemetry.set(definition.repository, null); }
+ }
+
+ const data = {
+ schema: 'the-interdependency.website-sitrep-view',
+ version: '1.0.0',
+ snapshotAt: new Date().toISOString(),
+ fallback: false,
+ fallbackReason: null,
+ sourceSnapshotAt: null,
+ controlPlane: { ...controlPlane, executor: python },
+ portfolio,
+ missingReports,
+ projects: projectDefinitions.map(definition => projectView(
+ definition,
+ reportRecords.get(definition.repository),
+ telemetry.get(definition.repository),
+ portfolioByRepo.get(definition.repository)
+ ))
+ };
+ await writeGenerated(data, true);
+ console.log(`sitrep ${data.projects.length} projects, ${missingReports.length} missing report(s), plan ${portfolio.portfolio_plan_sha256}`);
+} catch (error) {
+ const data = await fallbackData(publicFailureReason(error));
+ await writeGenerated(data, false);
+ console.log(`sitrep fallback: ${data.fallbackReason}`);
+} finally {
+ if (workDir) await rm(workDir, { recursive: true, force: true });
+}
\ No newline at end of file
diff --git a/src/_includes/layouts/base.njk b/src/_includes/layouts/base.njk
index effe1aa..788b5b9 100644
--- a/src/_includes/layouts/base.njk
+++ b/src/_includes/layouts/base.njk
@@ -12,6 +12,7 @@
+
@@ -27,6 +28,7 @@
Textbook
Narratives
Projects
+ SITREP
Artifacts
Works
Research
diff --git a/src/assets/css/sitrep.css b/src/assets/css/sitrep.css
new file mode 100644
index 0000000..117a729
--- /dev/null
+++ b/src/assets/css/sitrep.css
@@ -0,0 +1,173 @@
+.sitrep-head { max-width: 68rem; }
+.sitrep-control-plane,
+.sitrep-boundary {
+ display: grid;
+ grid-template-columns: minmax(0, 1.15fr) minmax(18rem, .85fr);
+ gap: 1.5rem;
+ align-items: start;
+ margin: 2rem 0 3.5rem;
+ padding: clamp(1.2rem, 3vw, 2rem);
+ border: 1px solid var(--violet);
+ border-radius: var(--radius);
+ background: linear-gradient(145deg, rgba(24, 28, 55, .96), rgba(10, 15, 29, .94));
+ box-shadow: var(--shadow);
+}
+.sitrep-control-plane h2,
+.sitrep-boundary h2 { margin: .25rem 0 .8rem; }
+.sitrep-contract-grid,
+.sitrep-metrics,
+.sitrep-observed-list { display: grid; gap: .55rem; margin: 0; }
+.sitrep-contract-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+.sitrep-contract-grid div,
+.sitrep-metrics div,
+.sitrep-observed-list div {
+ min-width: 0;
+ padding: .65rem .75rem;
+ border: 1px solid var(--line);
+ border-radius: .7rem;
+ background: rgba(9, 13, 24, .62);
+}
+.sitrep-contract-grid dt,
+.sitrep-metrics dt,
+.sitrep-observed-list dt {
+ color: var(--silver);
+ font: 700 .68rem/1.3 ui-monospace, SFMono-Regular, Consolas, monospace;
+ text-transform: uppercase;
+ letter-spacing: .08em;
+}
+.sitrep-contract-grid dd,
+.sitrep-metrics dd,
+.sitrep-observed-list dd { margin: .2rem 0 0; overflow-wrap: anywhere; }
+.sitrep-fallback { margin: 0 0 3rem; }
+.sitrep-fallback h2 { margin-top: 0; }
+.sitrep-grid { display: grid; gap: 1.35rem; margin: 1.6rem 0 4rem; }
+.sitrep-card {
+ overflow: hidden;
+ border: 1px solid var(--line);
+ border-radius: 1.2rem;
+ background: linear-gradient(145deg, rgba(16, 24, 42, .98), rgba(9, 13, 24, .96));
+ box-shadow: 0 20px 55px rgba(0, 0, 0, .2);
+}
+.sitrep-card-header {
+ display: grid;
+ grid-template-columns: 8.5rem minmax(0, 1fr);
+ gap: 1.3rem;
+ align-items: center;
+ padding: 1.35rem;
+ border-bottom: 1px solid var(--line);
+}
+.sitrep-card-header h3 { margin: .2rem 0 .35rem; font-size: clamp(1.6rem, 4vw, 2.5rem); }
+.sitrep-state { margin: 0; color: var(--silver); }
+.sitrep-orbit {
+ position: relative;
+ width: 7.25rem;
+ aspect-ratio: 1;
+ display: grid;
+ place-items: center;
+ border-radius: 50%;
+ background: radial-gradient(circle, rgba(155, 135, 245, .18) 0 23%, transparent 24% 100%);
+}
+.sitrep-orbit-ring,
+.sitrep-orbit::before,
+.sitrep-orbit::after {
+ content: "";
+ position: absolute;
+ border: 1px solid var(--line);
+ border-radius: 50%;
+}
+.sitrep-orbit-ring { inset: .35rem; border-color: var(--violet); }
+.sitrep-orbit::before { inset: 1.25rem; border-color: rgba(123, 201, 216, .72); }
+.sitrep-orbit::after { inset: 2.3rem; border-color: rgba(255, 107, 115, .8); }
+.sitrep-orbit strong {
+ position: relative;
+ z-index: 2;
+ max-width: 4.8rem;
+ text-align: center;
+ color: var(--starlight);
+ font: 800 .72rem/1.05 ui-monospace, SFMono-Regular, Consolas, monospace;
+ overflow-wrap: anywhere;
+}
+.orbit-marker {
+ position: absolute;
+ z-index: 3;
+ width: 1.65rem;
+ aspect-ratio: 1;
+ display: grid;
+ place-items: center;
+ border: 1px solid var(--line);
+ border-radius: 50%;
+ background: var(--night);
+ color: var(--silver);
+ font: 800 .57rem/1 ui-monospace, SFMono-Regular, Consolas, monospace;
+}
+.orbit-report { top: -.1rem; left: calc(50% - .82rem); border-color: var(--violet); color: #c2b7ff; }
+.orbit-ci { right: -.05rem; top: calc(50% - .82rem); border-color: var(--cyan); color: var(--cyan); }
+.orbit-next { bottom: -.1rem; left: calc(50% - .82rem); border-color: #85d3a6; color: #9ce2b9; }
+.orbit-hmmm { left: -.05rem; top: calc(50% - .82rem); border-color: var(--amber); color: var(--amber); }
+.sitrep-dual { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(17rem, .65fr); }
+.sitrep-declared,
+.sitrep-observed { padding: 1.25rem 1.35rem; }
+.sitrep-declared { border-right: 1px solid var(--line); }
+.sitrep-observed { background: rgba(7, 11, 21, .48); }
+.sitrep-axis-label {
+ margin: 0 0 .9rem;
+ color: var(--cyan);
+ font: 800 .68rem/1.3 ui-monospace, SFMono-Regular, Consolas, monospace;
+ text-transform: uppercase;
+ letter-spacing: .1em;
+}
+.sitrep-observed .sitrep-axis-label { color: var(--silver); }
+.sitrep-claim { max-width: 75ch; margin-top: 0; }
+.sitrep-metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 1rem 0; }
+.sitrep-metrics dd { font: 800 1.35rem/1.2 Georgia, "Times New Roman", serif; }
+.sitrep-observed-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+.sitrep-details { margin: 0; border: 0; border-top: 1px solid var(--line); border-radius: 0; background: rgba(13, 19, 34, .78); }
+.sitrep-details > summary { cursor: pointer; color: var(--cyan); font-weight: 800; }
+.sitrep-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem 1.5rem; padding-top: 1rem; }
+.sitrep-detail-grid section { min-width: 0; }
+.sitrep-detail-grid h4 { margin: 0 0 .5rem; font-size: .78rem; color: var(--silver); text-transform: uppercase; letter-spacing: .08em; }
+.sitrep-detail-grid ul { margin: 0; padding-left: 1.2rem; }
+.sitrep-detail-grid li { margin: .55rem 0; }
+.sitrep-detail-grid small { display: block; margin-top: .2rem; color: var(--silver); }
+.sitrep-relations { grid-column: 1 / -1; }
+.sitrep-relations li { display: grid; grid-template-columns: minmax(12rem, .45fr) minmax(0, 1fr); gap: .5rem 1rem; padding: .75rem 0; border-bottom: 1px solid rgba(42, 56, 87, .65); }
+.sitrep-relations li:last-child { border-bottom: 0; }
+.sitrep-relations small { grid-column: 2; color: var(--cyan); }
+.sitrep-work-graph { margin: 4rem 0; }
+.relation-list { display: grid; gap: .7rem; padding: 0; list-style: none; }
+.relation-list li {
+ display: grid;
+ grid-template-columns: minmax(12rem, .75fr) auto minmax(12rem, .75fr) minmax(16rem, 1.5fr);
+ gap: .75rem;
+ align-items: center;
+ padding: .9rem 1rem;
+ border: 1px solid var(--line);
+ border-radius: .8rem;
+ background: rgba(16, 24, 42, .78);
+}
+.relation-list li > span { color: var(--violet); font-size: 1.35rem; }
+.relation-list strong { color: var(--silver); font-weight: 500; }
+.sitrep-boundary { grid-template-columns: 1fr; }
+@media (max-width: 820px) {
+ .sitrep-control-plane,
+ .sitrep-dual { grid-template-columns: 1fr; }
+ .sitrep-declared { border-right: 0; border-bottom: 1px solid var(--line); }
+ .relation-list li { grid-template-columns: 1fr auto 1fr; }
+ .relation-list strong { grid-column: 1 / -1; }
+}
+@media (max-width: 560px) {
+ .sitrep-card-header { grid-template-columns: 1fr; }
+ .sitrep-orbit { width: 6.5rem; }
+ .sitrep-contract-grid,
+ .sitrep-metrics,
+ .sitrep-observed-list,
+ .sitrep-detail-grid { grid-template-columns: 1fr 1fr; }
+ .sitrep-relations { grid-column: auto; }
+ .sitrep-relations li { display: block; }
+ .sitrep-relations small { display: block; }
+ .relation-list li { grid-template-columns: 1fr; }
+ .relation-list li > span { transform: rotate(90deg); justify-self: start; }
+}
+@media (prefers-reduced-motion: reduce) {
+ .sitrep-card { scroll-behavior: auto; }
+}
diff --git a/src/sitrep/index.njk b/src/sitrep/index.njk
new file mode 100644
index 0000000..79ae89c
--- /dev/null
+++ b/src/sitrep/index.njk
@@ -0,0 +1,143 @@
+---
+layout: layouts/base.njk
+title: Repository SITREP
+description: A live, provenance-preserving view of METAPAT, UCNS, EDCM, a0, and this website, derived from repo-owned reports through skill-lib and overlaid with separate GitHub telemetry.
+---
+
+ Interdependent work graph · live public projection
+ Repository SITREP
+ Each repository states its own situation. skill-lib supplies the frozen reporting contract and deterministic portfolio projection. GitHub activity is shown separately as observed telemetry; a passing workflow never upgrades a repository claim.
+
+ {{ generated.sitrep.projects | length }} participating views
+ {% if generated.sitrep.fallback %}fallback snapshot {% endif %}
+ {% if generated.sitrep.missingReports and generated.sitrep.missingReports | length %}{{ generated.sitrep.missingReports | length }} missing report(s) {% endif %}
+
+
+
+
+
+
Control-plane provenance
+
skill-lib / interdependent-work-graph
+
The control plane validates, orders, hashes, and projects repo-owned reports. It owns no METAPAT canon, UCNS mathematics, EDCM measurement validity, a0 runtime authority, or website publication claim.
+
+
+
Contract repository-plan-report {{ generated.sitrep.controlPlane.reportSchemaVersion }}
+
Schema blob {{ generated.sitrep.controlPlane.reportSchemaBlob | truncate(12, true, '') }}
+
skill-lib commit {{ generated.sitrep.controlPlane.commit | truncate(12, true, '') }}
+
Projection {% if generated.sitrep.portfolio %}{{ generated.sitrep.portfolio.portfolio_plan_sha256 | truncate(12, true, '') }}{% else %}hmmm {% endif %}
+
Snapshot {{ generated.sitrep.snapshotAt | dateOnly }}
+
Mode {% if generated.sitrep.fallback %}last-known-good / unavailable boundary{% else %}fresh build-time observation{% endif %}
+
+
+
+{% if generated.sitrep.fallback %}
+
+ Live refresh unavailable
+ {{ generated.sitrep.fallbackReason }}. Displayed data is explicitly marked fallback; missing current state is not reconstructed.
+
+{% endif %}
+
+
+ Five distinct authority surfaces
+ Current repository situations
+ The left side of each infogram is declared situation from the repository report. The right side is GitHub-observed activity at site build time. Report/head mismatch is shown rather than silently reconciled.
+
+
+ {% for project in generated.sitrep.projects %}
+
+
+
+
+
+ Declared · repo-owned
+ {% if project.report %}
+ {{ project.report.status.current_claim }}
+
+
Frontier {{ project.report.active_frontier | length }}
+
Next {{ project.report.next_actions | length }}
+
Blocked {{ project.report.blocked | length }}
+
hmmm {{ project.report.hmmm | length }}
+
+
+ report {{ project.reportFreshness }}
+ source {{ project.report.source.commit | truncate(9, true, '') }}
+
+ {% else %}
+ hmmm No report was supplied to the skill-lib projection. No project state has been synthesized.
+ {% endif %}
+
+
+
+ Observed · GitHub
+ {% if project.telemetry %}
+
+
HEAD {{ project.telemetry.head | truncate(9, true, '') }}
+
Branch {{ project.telemetry.branch }}
+
Open PRs {{ project.telemetry.openPullRequests }}
+
Last push {{ project.telemetry.pushedAt | dateOnly }}
+
Workflow {% if project.telemetry.latestWorkflow %}{{ project.telemetry.latestWorkflow.conclusion or project.telemetry.latestWorkflow.status or 'hmmm' }}{% else %}hmmm{% endif %}
+
+ {% if project.telemetry.latestWorkflow %}
+ CI {{ project.telemetry.latestWorkflow.conclusion or project.telemetry.latestWorkflow.status }}
+ {% endif %}
+ {% else %}
+ GitHub telemetry was unavailable at build time.
+ {% endif %}
+
+
+
+ {% if project.report %}
+
+ Frontier, next actions, blockers, relations, and hmmm
+
+
Active frontier {% for item in project.report.active_frontier %}{{ item }} {% else %}None declared. {% endfor %}
+
Next actions {% for item in project.report.next_actions %}{{ item.action }}{% if item.dependency %}Dependency: {{ item.dependency }} {% endif %} {% else %}None declared. {% endfor %}
+
Blocked {% for item in project.report.blocked %}{{ item }} {% else %}None declared. {% endfor %}
+
hmmm {% for item in project.report.hmmm %}{{ item }} {% else %}None declared. {% endfor %}
+
Cross-repository relations {% for relation in project.report.cross_repository_relations %}{{ relation.repository }}{{ relation.relation }} authority transfer: false {% else %}None declared. {% endfor %}
+
+
+ {% endif %}
+
+ {% endfor %}
+
+
+
+{% if generated.sitrep.portfolio %}
+
+ Deterministic relation projection
+ Current work graph
+ These edges are emitted from repo-owned relation declarations by the skill-lib projection. Connection does not transfer authority, proof status, measurement validity, empirical status, or publication ownership.
+
+ {% for relation in generated.sitrep.portfolio.cross_repository_dependencies %}
+ {{ relation.from }}→ {{ relation.to }}{{ relation.relation }}
+ {% else %}
+ No cross-repository relations were projected.
+ {% endfor %}
+
+
+{% endif %}
+
+
+ Invariant
+ Relation without collapse
+ Repositories expose their own claims and boundaries. skill-lib defines the reusable coordination contract. This website presents the projection and observes GitHub state. None of those roles silently inherits another role's authority.
+
diff --git a/tests/accessibility.spec.mjs b/tests/accessibility.spec.mjs
index ff9bf53..ef1da60 100644
--- a/tests/accessibility.spec.mjs
+++ b/tests/accessibility.spec.mjs
@@ -13,7 +13,7 @@ if (!firstLabUnit) throw new Error(`missing Lab canon unit ${articleLab[0].unit_
for (const route of [
'/', '/home/', '/preamble/', '/chapters/', '/chapters/chapter-zero/', '/chapters/chapter-seven/',
- '/articles/', '/articles/article-two/', '/narratives/', '/narratives/jack-and-diane/', '/way/', '/lab/', `/lab/${firstLabUnit.routeSlug}/`, '/projects/',
+ '/articles/', '/articles/article-two/', '/narratives/', '/narratives/jack-and-diane/', '/way/', '/lab/', `/lab/${firstLabUnit.routeSlug}/`, '/projects/', '/sitrep/',
'/artifacts/gonol-relationships/', '/artifacts/edcm-mathematics/', '/artifacts/four-cuts/', '/fallback/', '/research/method/'
]) {
test(`${route} has no serious or critical automated accessibility violations`, async ({ page }) => {
@@ -26,4 +26,4 @@ for (const route of [
const blocking = results.violations.filter(violation => ['serious', 'critical'].includes(violation.impact));
expect(blocking, JSON.stringify(blocking, null, 2)).toEqual([]);
});
-}
+}
\ No newline at end of file
diff --git a/tests/site.spec.mjs b/tests/site.spec.mjs
index 448f540..e56ad2e 100644
--- a/tests/site.spec.mjs
+++ b/tests/site.spec.mjs
@@ -27,6 +27,7 @@ const routes = [
['/lab/', /Rights Article laboratories/],
['/source/', /Source/],
['/projects/', /Projects/],
+ ['/sitrep/', /Repository SITREP/],
['/artifacts/', /Artifacts/],
['/artifacts/gonol-relationships/', /Public Gonol relationship lab/],
['/artifacts/edcm-mathematics/', /EDCM mathematical reference/],
@@ -212,15 +213,16 @@ test('Public Gonol lab preserves vesica pieces, triquetra pair receipts, and unr
await expect(page.locator('.gonol-pair:not([hidden])')).toHaveCount(1);
const activeOperandOutput = page.locator('.gonol-operand:not([hidden]) .gonol-output').first();
- await expect(activeOperandOutput.locator(':scope > .copy-button')).toHaveCount(1);
+ await expect(activeOperandOutput.locator(':scope > .field-actions > .copy-button')).toHaveCount(3);
const completeReceiptOutput = page.locator('[data-receipt-output]').locator('..');
- await expect(completeReceiptOutput.locator(':scope > .copy-button')).toHaveCount(1);
+ await expect(completeReceiptOutput.locator(':scope > .field-actions > .copy-button')).toHaveCount(3);
await page.locator('#gonol-payload-A').fill('root');
await page.locator('#gonol-payload-B').fill('root');
await page.locator('[data-comparison-policy]').selectOption('exact-utf8');
await expect(page.locator('[data-receipt-output]')).toContainText('"exact_utf8_equal": true');
- await expect(page.locator('[data-vector-output]')).toContainText('"code_point": "U+0072"');
+ await expect(page.locator('[data-vector-output]')).toContainText('"unicode_scalar_value": 114');
+ await expect(page.locator('[data-vector-output]')).toContainText('"source_value": "r"');
await page.locator('.gonol-segmented label', { has: page.locator('input[value="3"]') }).click();
await expect(page.locator('[data-gonol-status]')).toContainText('3 retained pairwise vesicas');
@@ -228,14 +230,18 @@ test('Public Gonol lab preserves vesica pieces, triquetra pair receipts, and unr
await expect(page.locator('[data-gonol-stage] [data-pair-intersection]')).toHaveCount(6);
await expect(page.locator('.gonol-pair:not([hidden])')).toHaveCount(3);
for (const pair of ['AB', 'BC', 'CA']) {
- await expect(page.locator(`.gonol-pair[data-pair="${pair}"]`)).toContainText(`vesica-${pair.toLowerCase()}`);
+ const pairCard = page.locator(`.gonol-pair[data-pair="${pair}"]`);
+ await expect(pairCard).toContainText(`Retained vesica ${pair[0]} ↔ ${pair[1]}`);
+ await expect(pairCard).toContainText(`"pair_id": "${pair[0]}-${pair[1]}"`);
}
await page.locator('.gonol-segmented label', { has: page.locator('input[value="7"]') }).click();
await expect(page.locator('[data-gonol-status]')).toContainText('geometry and pairing remain hmmm');
await expect(page.locator('[data-gonol-stage] .gonol-identity-box')).toHaveCount(7);
await expect(page.locator('[data-gonol-stage] .gonol-operand-ring')).toHaveCount(0);
- await expect(page.locator('[data-receipt-output]')).toContainText('no seven-form geometry or pairing count was inferred');
+ await expect(page.locator('[data-receipt-output]')).toContainText('"standing": "hmmm-unresolved"');
+ await expect(page.locator('[data-receipt-output]')).toContainText('"pairs": []');
+ await expect(page.locator('[data-receipt-output]')).toContainText('seven-gonol display geometry and pairing plan');
});
test('Public Gonol lab contains long receipts on a narrow viewport', async ({ page }) => {
@@ -270,4 +276,4 @@ test('Research is study-only and exposes provisional citation gaps', async ({ pa
await expect(page.locator('body')).toContainText('support no-qualifying-study-found');
await expect(page.locator('body')).toContainText('dissent no-qualifying-study-found');
await expect(page.locator('body')).not.toContainText('TeamSTEPPS');
-});
+});
\ No newline at end of file
diff --git a/tests/sitrep.test.mjs b/tests/sitrep.test.mjs
new file mode 100644
index 0000000..1840615
--- /dev/null
+++ b/tests/sitrep.test.mjs
@@ -0,0 +1,49 @@
+import assert from 'node:assert/strict';
+import { readFile } from 'node:fs/promises';
+import test from 'node:test';
+
+test('SITREP consumes the frozen skill-lib projection instead of redefining repo authority', async () => {
+ const source = await readFile('scripts/fetch-sitrep.mjs', 'utf8');
+ assert.match(source, /repository-plan-report\.schema\.json/);
+ assert.match(source, /9b347b2dff7692054b571602f30ee6d00c2e7265/);
+ assert.match(source, /interdependent-work-graph\/portfolio_plan\.py/);
+ assert.match(source, /97b8b546b4151486164c8a4b730c24a8c895b25b/);
+ assert.match(source, /runPortfolio/);
+ assert.match(source, /python3/);
+ assert.match(source, /missingReports/);
+});
+
+test('SITREP failure publication classifies errors instead of echoing command details', async () => {
+ const source = await readFile('scripts/fetch-sitrep.mjs', 'utf8');
+ assert.match(source, /function publicFailureReason/);
+ assert.match(source, /fallbackData\(publicFailureReason\(error\)\)/);
+ assert.doesNotMatch(source, /fallbackData\(error\.message/);
+ assert.doesNotMatch(source, /reason:\s*error\.message/);
+});
+
+test('HEAD mismatch is exposed as difference without inferring substantive staleness', async () => {
+ const source = await readFile('scripts/fetch-sitrep.mjs', 'utf8');
+ assert.match(source, /sourceCommit === head \? 'current' : 'HEAD differs'/);
+ assert.doesNotMatch(source, /sourceCommit === head \? 'current' : 'stale'/);
+});
+
+test('SITREP presentation keeps declared situation separate from observed GitHub telemetry', async () => {
+ const template = await readFile('src/sitrep/index.njk', 'utf8');
+ assert.match(template, /Declared · repo-owned/);
+ assert.match(template, /Observed · GitHub/);
+ assert.match(template, /Report\/head mismatch is shown rather than silently reconciled/);
+ assert.match(template, /authority transfer: false/);
+});
+
+test('website participates through the skill-lib repository-plan-report contract', async () => {
+ const report = JSON.parse(await readFile('docs/work-graphs/repository-plan-report.json', 'utf8'));
+ assert.equal(report.schema, 'the-interdependency.repository-plan-report');
+ assert.equal(report.version, '1.0.0');
+ assert.equal(report.repository, 'The-Interdependency/The-Interdependency.github.io');
+ assert.equal(report.contract.repository, 'The-Interdependency/skill-lib');
+ assert.equal(report.contract.path, 'interdependent-work-graph/repository-plan-report.schema.json');
+ assert.equal(report.contract.blob_sha, '9b347b2dff7692054b571602f30ee6d00c2e7265');
+ assert.equal(report.portfolio_role.reports_to.skill, 'interdependent-work-graph');
+ assert.match(report.source.commit, /^[0-9a-f]{40}$/);
+ assert.ok(report.authority.non_transfer.length > 0);
+});
\ No newline at end of file