diff --git a/.claude/skills/agent-team-readiness/references/publishing.md b/.claude/skills/agent-team-readiness/references/publishing.md index 558b6fa..3dcc237 100644 --- a/.claude/skills/agent-team-readiness/references/publishing.md +++ b/.claude/skills/agent-team-readiness/references/publishing.md @@ -64,7 +64,7 @@ if [ "$VISIBILITY" != "PUBLIC" ]; then exit 1 fi -KEY="$(node scripts/render-report.mjs "$OUT/atr-1.json" --out-dir "$OUT")" +KEY="$(node scripts/render-report.mjs "$OUT/atr-1.json" --out-dir "$OUT" --hosted)" test -n "$KEY" test -f "$OUT/$KEY.html" ``` @@ -78,6 +78,12 @@ The HTML is self-contained, has no script or external asset dependency, uses a restrictive Content Security Policy, escapes every report string, and links only to the matching machine-readable JSON object. +`--hosted` is allowed only after the visibility gate above. It makes the HTML +link to `./.json`, the object name used by the JSON-first upload. +Without `--hosted`, a same-directory local render links to the actual input +JSON filename; a render into another directory omits the machine link rather +than inventing a path that does not exist. + ## JSON-first gated upload Credentials come from the hosted runtime. Do not hardcode a key, secret, diff --git a/.claude/skills/agent-team-readiness/scripts/render-report.mjs b/.claude/skills/agent-team-readiness/scripts/render-report.mjs index d026ea6..8fbdd67 100755 --- a/.claude/skills/agent-team-readiness/scripts/render-report.mjs +++ b/.claude/skills/agent-team-readiness/scripts/render-report.mjs @@ -7,8 +7,6 @@ import process from "node:process"; import { fileURLToPath } from "node:url"; import { validateReport } from "./lib/validate.mjs"; -export const REPORT_BASE = "https://report.first-tree.ai"; - // Strip controls and bidirectional overrides at the rendering sink. Repository // names and evidence are untrusted even after the atr-1 shape is validated. // eslint-disable-next-line no-control-regex @@ -102,78 +100,134 @@ function renderEvidenceList(entries) { return ``; } -function renderTopFix(fix, index) { - return ` -
- -
-
- ${escapeHtml(severityLabel(fix.severity), 16)} - ${escapeHtml(fix.dimension.replaceAll("_", " "), 80)} -
-

${escapeHtml(fix.title, 240)}

-

${escapeHtml(fix.why_it_matters, 800)}

-
-
Minimum fix
${escapeHtml(fix.minimum_fix, 1000)}
-
Verify first
${escapeHtml(fix.first_verification_step, 1000)}
-
-
-
`; +function dimensionTone(dimension) { + if (dimension.status === "constrained") return { label: "Constrained", className: "critical" }; + if (dimension.status === "developing") return { label: "Developing", className: "high" }; + return { label: "Strong", className: "stable" }; +} + +function renderDimensionRail(dimension) { + const tone = dimensionTone(dimension); + return `
+ ${escapeHtml(dimension.name, 120)}${dimension.weight}% weight + ${dimension.score}/10 + +
`; +} + +function renderPriorityRow(fix, index) { + return `
+ ${String(index + 1).padStart(2, "0")} + ${escapeHtml(fix.title, 240)} + ${escapeHtml(fix.dimension.replaceAll("_", " "), 80)} + ${escapeHtml(severityLabel(fix.severity), 16)} + ${escapeHtml(fix.why_it_matters, 360)} +
`; } -function renderDimension(dimension, index) { +function renderDimensionDetail(dimension, className = "dimension-detail") { const blocker = dimension.must_fix_blocker; const blockerMarkup = blocker - ? `
- ${escapeHtml(severityLabel(blocker.severity), 16)} blocker + ? `
+

Dimension blocker

${escapeHtml(severityLabel(blocker.severity), 16)}
${escapeHtml(blocker.title, 240)} -

${escapeHtml(blocker.why_it_matters, 800)}

-
` - : `
No repository-observable blocker found
`; - - return ` -
-
-
- Dimension ${index + 1} · ${dimension.weight}% weight -

${escapeHtml(dimension.name, 120)}

-
-
- ${dimension.score}/10 -
-
-
- ${escapeHtml(dimension.status, 20)} - ${escapeHtml(dimension.evidence_status, 20)} evidence +

${escapeHtml(blocker.why_it_matters, 1000)}

+
Blocker evidence
+ ${blocker.evidence.length ? renderEvidenceList(blocker.evidence) : "

No blocker evidence was retained.

"} + ` + : ""; + return `
${blockerMarkup} +

Strongest evidence

${renderEvidenceList(dimension.strongest_evidence)}
+

Smallest useful improvement

${escapeHtml(dimension.minimum_improvement, 1000)}

+

What remains unknown

    ${dimension.unknowns.map((unknown) => `
  • ${escapeHtml(unknown, 1000)}
  • `).join("")}
+
`; +} + +function renderDimensionLedger(dimension, index) { + const tone = dimensionTone(dimension); + return `
+
+ ${String(dimension.score).padStart(2, "0")} +
+

${escapeHtml(dimension.name, 120)}

+

${escapeHtml(dimension.rationale, 1000)}

-

${escapeHtml(dimension.rationale, 1000)}

- ${blockerMarkup} -
- Smallest useful improvement -

${escapeHtml(dimension.minimum_improvement, 1000)}

+
+ Evidence + ${dimension.strongest_evidence.length} source${dimension.strongest_evidence.length === 1 ? "" : "s"} + ${escapeHtml(dimension.evidence_status, 20)} coverage
-
- Strongest evidence - ${renderEvidenceList(dimension.strongest_evidence)} -
-
- What remains unknown -
    ${dimension.unknowns.map((unknown) => `
  • ${escapeHtml(unknown, 1000)}
  • `).join("")}
-
-
`; +
+ Status + ${tone.label} +
+
+
+ Open evidence and next step + ${renderDimensionDetail(dimension)} +
+ ${renderDimensionDetail(dimension, "print-dimension-detail")} +
`; } -export function renderReport(report) { +function renderFixChapter(fix, index) { + const evidence = fix.evidence ?? []; + return `
+
+ ${String(index + 1).padStart(2, "0")} +
${escapeHtml(fix.dimension.replaceAll("_", " "), 80)}

${escapeHtml(fix.title, 240)}

${escapeHtml(fix.why_it_matters, 800)}

+ ${escapeHtml(severityLabel(fix.severity), 16)} +
+
+

Blocker evidence

${evidence.length ? renderEvidenceList(evidence) : "

No blocker evidence was retained.

"}
+

Why it matters

${escapeHtml(fix.why_it_matters, 1000)}

+

Minimum fix

${escapeHtml(fix.minimum_fix, 1000)}

+

Verify first

${escapeHtml(fix.first_verification_step, 1000)}

+
+
`; +} + +function validateMachineHref(machineHref) { + if (machineHref === null) return null; + if (!/^\.\/[A-Za-z0-9._~%+-]+\.json$/.test(machineHref)) { + throw new Error("machine JSON href must be a same-directory JSON path"); + } + return machineHref; +} + +export function renderReport(report, { machineHref = null } = {}) { validateReport(report); const key = computeReportKey(report); const score = report.headline_score; const scoreText = score === null ? "—" : String(score); - const scoreValue = score === null ? 0 : score; const revision = report.repository.revision ? report.repository.revision.slice(0, 12) : report.repository.worktree_state; const generatedDate = report.generated_at.slice(0, 10); - const fixes = report.top_3_fixes.length - ? report.top_3_fixes.map(renderTopFix).join("") - : `
No must-fix blocker ranked in the Top 3.

This is not certification. Runtime isolation, permissions, team behavior, and private integrations remain outside the repository-observable claim.

`; + const verdict = score === null ? "Repository-observable score withheld" : "Repository-observable readiness"; + const scoreTone = score === null + ? "neutral" + : report.dimensions.some((dimension) => dimension.status === "constrained") + ? "danger" + : report.dimensions.some((dimension) => dimension.status === "developing") + ? "warning" + : "stable"; + const machineUrl = validateMachineHref(machineHref); + const machineAction = machineUrl + ? `Open machine-readable atr-1 JSON` + : `Review scan limitations`; + const priorityRows = report.top_3_fixes.length + ? report.top_3_fixes.map(renderPriorityRow).join("") + : `
No repository-observable blocker ranked in the Top 3.
`; + const fixChapters = report.top_3_fixes.length + ? report.top_3_fixes.map((fix, index) => renderFixChapter(fix, index)).join("") + : `
No must-fix chapter was generated from observed evidence.

Review evidence coverage and remaining unknowns before treating this result as complete or as runtime certification.

`; + const mustFixHeading = report.top_3_fixes.length + ? `
Must-fix chapters · Top ${report.top_3_fixes.length} blockers

Fix these in order

+

Each chapter connects blocker evidence to its impact, minimum fix, and first verification step.

` + : `
Observed evidence

No repository-observable blockers were ranked

+

Review evidence coverage and remaining unknowns before treating repository evidence as complete or as runtime certification.

`; + const nextStep = report.top_3_fixes.length + ? `` + : ``; return ` @@ -185,18 +239,18 @@ export function renderReport(report) { ${escapeHtml(report.repository.name, 180)} · Agent Team Readiness @@ -355,49 +433,68 @@ export function renderReport(report) {
-
A/1Agent Team Readiness
- Repository-observable assessment +
first-tree / agent team readiness
+ Evidence-first repository scan
- atr-1 · Evidence-first scan + Agent team readiness

${escapeHtml(report.repository.name, 180)}

+
${scoreText}${score === null ? "" : "/100"}
+

${escapeHtml(verdict, 80)}

${escapeHtml(report.summary, 1200)}

Revision ${escapeHtml(revision, 80)} Generated ${escapeHtml(generatedDate, 16)} - Worktree ${escapeHtml(report.repository.worktree_state, 20)} + Files ${report.repository.analyzed_file_count.toLocaleString("en-US")} + Evidence ${report.scope.evidence_coverage}%
-
-

Read this score narrowly. It measures repository-level support for parallel coding agents. It does not certify runtime isolation, permissions, owner availability, or actual team behavior.

+ -
-
Evidence coverage${report.scope.evidence_coverage}%
-
Files analyzed${report.repository.analyzed_file_count.toLocaleString("en-US")}
-
Top blockers${report.top_3_fixes.length}
+
+ What this score means +

Repository readiness, not runtime certification.

+

${score === null ? "The headline score is withheld because the repository inventory is incomplete." : `${score}/100 reflects repository-observable support across six fixed dimensions for parallel coding agents.`} ${report.top_3_fixes.length ? "Start with the highest-priority blockers below, then use the evidence ledger to understand what remains unknown." : "Use the evidence ledger to review the repository contract and what remains unknown."}

+

This scan does not certify runtime isolation, permissions, owner availability, actual team behavior, or private integrations.

-
+
-
Prioritized work

Top 3 readiness fixes

-

Ranked deterministically by blocker severity, dimension weight, and the gap to a strong repository contract.

+
Where repository support is weakest

Highest-priority repository blockers

+

Ranked by blocker severity, dimension weight, and evidence gap.

-
${fixes}
+
+
PriorityBlockerDimensionSeverityImpact
+ ${priorityRows} +
+
+ +
+
+
Six dimensions · Full results

The repository contract, line by line

+

Every score keeps its evidence, next useful improvement, and unknowns available.

+
+
${report.dimensions.map(renderDimensionLedger).join("")}
-
+
-
Six fixed dimensions

Where the repository helps—or gets in the way

-

Each dimension keeps its strongest evidence, smallest useful improvement, and unknowns separate.

+ ${mustFixHeading}
-
${report.dimensions.map(renderDimension).join("")}
+ ${fixChapters}
@@ -406,8 +503,10 @@ export function renderReport(report) {
    ${report.scope.limitations.map((limitation) => `
  • ${escapeHtml(limitation, 1000)}
  • `).join("")}
+ ${nextStep} +
@@ -415,18 +514,21 @@ export function renderReport(report) { } function usage() { - return "Usage: render-report.mjs [--out-dir ]"; + return "Usage: render-report.mjs [--out-dir ] [--hosted]"; } function parseArgs(argv) { if (argv.includes("--help") || argv.includes("-h")) return { help: true }; let reportFile = null; let outputDirectory = null; + let hosted = false; for (let index = 0; index < argv.length; index += 1) { const argument = argv[index]; if (argument === "--out-dir") { outputDirectory = argv[++index]; if (!outputDirectory) throw new Error("--out-dir requires a directory"); + } else if (argument === "--hosted") { + hosted = true; } else if (argument.startsWith("-")) { throw new Error(`unknown option: ${argument}`); } else if (reportFile === null) { @@ -436,17 +538,23 @@ function parseArgs(argv) { } } if (!reportFile) throw new Error("atr-1.json path is required"); - return { reportFile: path.resolve(reportFile), outputDirectory }; + return { reportFile: path.resolve(reportFile), outputDirectory, hosted }; } -export async function renderReportFile(reportFile, outputDirectory = null) { +export async function renderReportFile(reportFile, outputDirectory = null, { hosted = false } = {}) { const report = JSON.parse(await readFile(reportFile, "utf8")); validateReport(report); const key = computeReportKey(report); const directory = path.resolve(outputDirectory ?? path.dirname(reportFile)); await mkdir(directory, { recursive: true }); const outputFile = path.join(directory, `${key}.html`); - await writeFile(outputFile, renderReport(report), { encoding: "utf8", flag: "wx", mode: 0o600 }); + const sameDirectory = path.dirname(path.resolve(reportFile)) === directory; + const machineHref = hosted + ? `./${key}.json` + : sameDirectory + ? `./${encodeURIComponent(path.basename(reportFile))}` + : null; + await writeFile(outputFile, renderReport(report, { machineHref }), { encoding: "utf8", flag: "wx", mode: 0o600 }); return { key, outputFile }; } @@ -457,7 +565,7 @@ export async function runCli(argv) { process.stdout.write(`${usage()}\n`); return; } - const result = await renderReportFile(options.reportFile, options.outputDirectory); + const result = await renderReportFile(options.reportFile, options.outputDirectory, { hosted: options.hosted }); process.stdout.write(`${result.key}\n`); } catch (error) { process.stderr.write(`atr-render: ${error.message}\n`); diff --git a/examples/first-tree/report.html b/examples/first-tree/report.html index 7d3db1e..b1933cc 100644 --- a/examples/first-tree/report.html +++ b/examples/first-tree/report.html @@ -8,18 +8,18 @@ agent-team-foundation/first-tree · Agent Team Readiness @@ -178,223 +202,273 @@
-
A/1Agent Team Readiness
- Repository-observable assessment +
first-tree / agent team readiness
+ Evidence-first repository scan
- atr-1 · Evidence-first scan + Agent team readiness

agent-team-foundation/first-tree

+
84/100
+

Repository-observable readiness

Repo-level score 84/100 at 99% evidence coverage. Strong: Instruction convergence, Shared decisions and context, Repeatable verification, Handoff and definition of done. Constrained: none. 0 prioritized readiness fixes.

Revision 2d5bacb917c2 Generated 2026-07-16 - Worktree clean + Files 2,079 + Evidence 99%
-
-

Read this score narrowly. It measures repository-level support for parallel coding agents. It does not certify runtime isolation, permissions, owner availability, or actual team behavior.

+ -
-
Evidence coverage99%
-
Files analyzed2,079
-
Top blockers0
+
+ What this score means +

Repository readiness, not runtime certification.

+

84/100 reflects repository-observable support across six fixed dimensions for parallel coding agents. Use the evidence ledger to review the repository contract and what remains unknown.

+

This scan does not certify runtime isolation, permissions, owner availability, actual team behavior, or private integrations.

-
+
-
Prioritized work

Top 3 readiness fixes

-

Ranked deterministically by blocker severity, dimension weight, and the gap to a strong repository contract.

+
Where repository support is weakest

Highest-priority repository blockers

+

Ranked by blocker severity, dimension weight, and evidence gap.

+
+
+
PriorityBlockerDimensionSeverityImpact
+
No repository-observable blocker ranked in the Top 3.
-
No must-fix blocker ranked in the Top 3.

This is not certification. Runtime isolation, permissions, team behavior, and private integrations remain outside the repository-observable claim.

-
+
-
Six fixed dimensions

Where the repository helps—or gets in the way

-

Each dimension keeps its strongest evidence, smallest useful improvement, and unknowns separate.

+
Six dimensions · Full results

The repository contract, line by line

+

Every score keeps its evidence, next useful improvement, and unknowns available.

-
-
-
-
- Dimension 1 · 18% weight -

Instruction convergence

-
-
- 10/10 -
-
-
- strong - partial evidence +
+
+ 10 +
+

Instruction convergence

+

1 root and 1 scoped instruction sources; root has 8 commands and 8 boundary signals; 0 detected root conflicts.

-

1 root and 1 scoped instruction sources; root has 8 commands and 8 boundary signals; 0 detected root conflicts.

-
No repository-observable blocker found
-
- Smallest useful improvement -

Keep one canonical root instruction source and use scoped files only for real local overrides.

+
+ Evidence + 1 source + partial coverage
-
- Strongest evidence -
  • AGENTS.md:1Root-scoped agent instruction source
-
-
- What remains unknown -
  • Whether every agent runtime actually loads the discovered instruction files is not observable from the repository.
-
-
-
-
-
- Dimension 2 · 18% weight -

Task and workspace isolation

-
-
- 7/10 -
-
-
- developing - partial evidence +
+ Status + Strong +
+
+
+ Open evidence and next step +
+

Strongest evidence

  • AGENTS.md:1Root-scoped agent instruction source
+

Smallest useful improvement

Keep one canonical root instruction source and use scoped files only for real local overrides.

+

What remains unknown

  • Whether every agent runtime actually loads the discovered instruction files is not observable from the repository.
+
+
+ +
+
+ 07 +
+

Task and workspace isolation

+

0 root-strong, 1 scoped-strong, 1 branch-only, and 0 unsafe isolation policy signals; 2 workspace manifests and 1 environment-isolation files.

-

0 root-strong, 1 scoped-strong, 1 branch-only, and 0 unsafe isolation policy signals; 2 workspace manifests and 1 environment-isolation files.

-
No repository-observable blocker found
-
- Smallest useful improvement -

Add explicit port, credential, cache, and generated-output boundaries to the existing branch/worktree policy.

+
+ Evidence + 3 sources + partial coverage
-
- Strongest evidence -
  • AGENTS.md:106Task/workspace isolation policy
  • pnpm-workspace.yaml:1Repository workspace boundary manifest
  • docker-compose.yml:1Repository-local environment isolation surface
-
-
- What remains unknown -
  • Actual concurrent worktree, branch, credential, port, and temporary-state isolation is not observable from repository files alone.
-
-
-
-
-
- Dimension 3 · 16% weight -

Code and domain ownership

-
-
- 6/10 -
-
-
- developing - partial evidence +
+ Status + Developing
-

1 actionable CODEOWNERS/OWNERS files with 1 rules, 0 ownership docs, and 15 instruction boundaries.

-
No repository-observable blocker found
-
- Smallest useful improvement -

Cover any unmapped high-risk, generated, migration, and release paths without imposing blanket per-file gating.

+
+
+ Open evidence and next step +
+

Strongest evidence

  • AGENTS.md:106Task/workspace isolation policy
  • pnpm-workspace.yaml:1Repository workspace boundary manifest
  • docker-compose.yml:1Repository-local environment isolation surface
+

Smallest useful improvement

Add explicit port, credential, cache, and generated-output boundaries to the existing branch/worktree policy.

+

What remains unknown

  • Actual concurrent worktree, branch, credential, port, and temporary-state isolation is not observable from repository files alone.
+
+
+ +
+
+ 06 +
+

Code and domain ownership

+

1 actionable CODEOWNERS/OWNERS files with 1 rules, 0 ownership docs, and 15 instruction boundaries.

-
- Strongest evidence -
  • .github/CODEOWNERS:11 ownership rules
  • AGENTS.md:40Explicit edit or scope boundary
-
-
- What remains unknown -
  • Whether named owners are current, available, and required reviewers is not observable from static repository evidence.
-
-
-
-
-
- Dimension 4 · 16% weight -

Shared decisions and context

-
-
- 8/10 -
-
-
- strong - partial evidence +
+ Evidence + 2 sources + partial coverage
-

1 architecture docs, 0 decision records, 7 module docs, and 1 Context Tree signals.

-
No repository-observable blocker found
-
- Smallest useful improvement -

Link the root module map to current decisions and retire stale implementation walkthroughs from the decision surface.

+
+ Status + Developing
-
- Strongest evidence -
  • packages/web/DESIGN.md:1Architecture or design context
  • README.md:200Root architecture or repository-map reference
-
-
- What remains unknown -
  • Whether decision material is current, routinely consulted, or backed by private shared context is not fully observable.
-
-
-
-
-
- Dimension 5 · 20% weight -

Repeatable verification

-
-
- 10/10 -
-
-
- strong - partial evidence +
+
+ Open evidence and next step +
+

Strongest evidence

  • .github/CODEOWNERS:11 ownership rules
  • AGENTS.md:40Explicit edit or scope boundary
+

Smallest useful improvement

Cover any unmapped high-risk, generated, migration, and release paths without imposing blanket per-file gating.

+

What remains unknown

  • Whether named owners are current, available, and required reviewers is not observable from static repository evidence.
+
+
+ +
+
+ 08 +
+

Shared decisions and context

+

1 architecture docs, 0 decision records, 7 module docs, and 1 Context Tree signals.

-

23 canonical command candidates across 4 action types, 4 CI files, 765 test files, and 2 lockfiles.

-
No repository-observable blocker found
-
- Smallest useful improvement -

Document a clean-checkout verification sequence and keep local commands identical to required CI gates.

+
+ Evidence + 2 sources + partial coverage
-
- Strongest evidence -
  • pnpm --dir apps/cli testapps/cli/package.json:44 · test script declared in package manifest
  • pnpm --dir apps/cli typecheckapps/cli/package.json:43 · typecheck script declared in package manifest
  • pnpm --dir apps/cli buildapps/cli/package.json:42 · build script declared in package manifest
  • .github/workflows/ci.yml:1Continuous integration configuration
-
-
- What remains unknown -
  • The default scan detects commands and CI configuration but does not execute target-repository commands. Runtime pass/fail remains unknown.
-
-
-
-
-
- Dimension 6 · 12% weight -

Handoff and definition of done

-
-
- 9/10 -
-
-
- strong - partial evidence +
+ Status + Strong +
+
+
+ Open evidence and next step +
+

Strongest evidence

  • packages/web/DESIGN.md:1Architecture or design context
  • README.md:200Root architecture or repository-map reference
+

Smallest useful improvement

Link the root module map to current decisions and retire stale implementation walkthroughs from the decision surface.

+

What remains unknown

  • Whether decision material is current, routinely consulted, or backed by private shared context is not fully observable.
+
+
+ +
+
+ 10 +
+

Repeatable verification

+

23 canonical command candidates across 4 action types, 4 CI files, 765 test files, and 2 lockfiles.

+
+
+ Evidence + 4 sources + partial coverage +
+
+ Status + Strong +
+
+
+ Open evidence and next step +
+

Strongest evidence

  • pnpm --dir apps/cli testapps/cli/package.json:44 · test script declared in package manifest
  • pnpm --dir apps/cli typecheckapps/cli/package.json:43 · typecheck script declared in package manifest
  • pnpm --dir apps/cli buildapps/cli/package.json:42 · build script declared in package manifest
  • .github/workflows/ci.yml:1Continuous integration configuration
+

Smallest useful improvement

Document a clean-checkout verification sequence and keep local commands identical to required CI gates.

+

What remains unknown

  • The default scan detects commands and CI configuration but does not execute target-repository commands. Runtime pass/fail remains unknown.
+
+
+ +
+
+ 09 +
+

Handoff and definition of done

+

2 issue templates, 1 pull request templates, 1 acceptance signals, and 0 verification signals.

+
+
+ Evidence + 3 sources + partial coverage
-

2 issue templates, 1 pull request templates, 1 acceptance signals, and 0 verification signals.

-
No repository-observable blocker found
-
- Smallest useful improvement -

Keep handoff templates short but require acceptance criteria, verification evidence, risk, and unresolved unknowns.

+
+ Status + Strong +
+
+
+ Open evidence and next step +
+

Strongest evidence

  • .github/ISSUE_TEMPLATE/bug_report.md:1Issue handoff template
  • .github/PULL_REQUEST_TEMPLATE.md:1Pull request completion template
  • CONTRIBUTING.md:65Acceptance or definition-of-done signal
+

Smallest useful improvement

Keep handoff templates short but require acceptance criteria, verification evidence, risk, and unresolved unknowns.

+

What remains unknown

  • The quality of real tasks, review conversations, and completion behavior outside repository templates remains unknown.
+
+
+ +
+
+ +
+
+
Observed evidence

No repository-observable blockers were ranked

+

Review evidence coverage and remaining unknowns before treating repository evidence as complete or as runtime certification.

-
- Strongest evidence -
  • .github/ISSUE_TEMPLATE/bug_report.md:1Issue handoff template
  • .github/PULL_REQUEST_TEMPLATE.md:1Pull request completion template
  • CONTRIBUTING.md:65Acceptance or definition-of-done signal
-
-
- What remains unknown -
  • The quality of real tasks, review conversations, and completion behavior outside repository templates remains unknown.
-
- +
No must-fix chapter was generated from observed evidence.

Review evidence coverage and remaining unknowns before treating this result as complete or as runtime certification.

@@ -403,8 +477,10 @@

What this scan cannot prove

  • The conclusion is limited to repository-observable readiness.
  • Actual concurrent workspace enforcement, organization permissions, team behavior, and private SaaS configuration remain unknown.
  • Detected verification commands are not executed by the default read-only scan.
  • Generated AGENTS.md and Context Tree materials are review drafts; the scanner never writes them into the target repository.
+ +
-
Report key: agent-team-foundation-first-tree-20260716-5cfd9236Open machine-readable atr-1 JSON
+
first-tree · Agent Team ReadinessReport key: agent-team-foundation-first-tree-20260716-5cfd9236
diff --git a/tests/report-renderer.test.mjs b/tests/report-renderer.test.mjs index 2a84123..e386978 100644 --- a/tests/report-renderer.test.mjs +++ b/tests/report-renderer.test.mjs @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import test from "node:test"; @@ -44,7 +44,7 @@ test("renderer escapes untrusted report strings and emits a self-contained acces report.summary = "Readiness & review"; report.dimensions[0].rationale = "Rationale "; report.dimensions[0].unknowns = ["Unknown "]; - const html = renderReport(report); + const html = renderReport(report, { machineHref: "./atr-1.json" }); assert.doesNotMatch(html, /Priority/); + assert.match(html, /aria-colspan="5"/); + assert.match(html, /overflow-wrap: anywhere/); + assert.match(html, /--accent: #3f5200/); + assert.match(html, /--warning: #8f3f00/); assert.match(html, /@media \(prefers-reduced-motion: reduce\)/); assert.match(html, /@media print/); assert.match(html, /Open machine-readable atr-1 JSON/); - for (const dimension of report.dimensions) assert.match(html, new RegExp(escapeHtml(dimension.name))); + for (const dimension of report.dimensions) { + assert.match(html, new RegExp(escapeHtml(dimension.name))); + assert.match( + html, + new RegExp(`${escapeHtml(dimension.name)}${dimension.weight}% weight`) + ); + } }); test("checked First Tree HTML sample is byte-reproducible", async () => { @@ -72,12 +95,35 @@ test("renderer writes one deterministic HTML artifact and refuses an existing de assert.equal(path.basename(first.outputFile), `${first.key}.html`); const html = await readFile(first.outputFile, "utf8"); assert.match(html, new RegExp(first.key)); + assert.match(html, /href="\.\/atr-1\.json"/); await assert.rejects(renderReportFile(reportFile, sandbox), /EEXIST/); } finally { await rm(sandbox, { recursive: true, force: true }); } }); +test("renderer never invents a local machine JSON link across directories", async () => { + const sandbox = await mkdtemp(path.join(os.tmpdir(), "atr-render-link-")); + try { + const reportDirectory = path.join(sandbox, "input"); + const outputDirectory = path.join(sandbox, "output"); + await mkdir(reportDirectory); + const reportFile = path.join(reportDirectory, "atr-1.json"); + await writeFile(reportFile, `${JSON.stringify(SAMPLE)}\n`); + const local = await renderReportFile(reportFile, outputDirectory); + const localHtml = await readFile(local.outputFile, "utf8"); + assert.doesNotMatch(localHtml, /Open machine-readable atr-1 JSON/); + assert.match(localHtml, /Review scan limitations/); + + const hostedDirectory = path.join(sandbox, "hosted"); + const hosted = await renderReportFile(reportFile, hostedDirectory, { hosted: true }); + const hostedHtml = await readFile(hosted.outputFile, "utf8"); + assert.match(hostedHtml, new RegExp(`href="\\./${hosted.key}\\.json"`)); + } finally { + await rm(sandbox, { recursive: true, force: true }); + } +}); + test("withheld headline scores stay visibly withheld", () => { const report = cloneSample(); report.repository.file_count = 10; @@ -89,9 +135,12 @@ test("withheld headline scores stay visibly withheld", () => { const html = renderReport(report); assert.match(html, /score withheld/); assert.doesNotMatch(html, /repo score \/ 100/); + assert.match(html, /No must-fix chapter was generated from observed evidence/); + assert.doesNotMatch(html, /Evidence ledger complete/); + assert.doesNotMatch(html, /No must-fix chapter is required/); }); -test("scanner blockers render in their deterministic Top 3 order", async () => { +test("scanner renders every dimension blocker while preserving deterministic Top 3 order", async () => { const sandbox = await mkdtemp(path.join(os.tmpdir(), "atr-render-blockers-")); try { const result = await runScan({ @@ -101,6 +150,23 @@ test("scanner blockers render in their deterministic Top 3 order", async () => { }); assert.ok(result.report.top_3_fixes.length > 0); const html = renderReport(result.report); + assert.match(html, /score-lockup tone-danger/); + assert.match(html, /Review the must-fix chapters/); + assert.match(html, /Blocker evidence/); + assert.match(html, /No root-scoped agent instruction source was found/); + assert.match(html, /Constrained/); + assert.doesNotMatch(html, /Root cause/); + const dimensionBlockers = result.report.dimensions + .map((dimension) => dimension.must_fix_blocker) + .filter(Boolean); + assert.ok(dimensionBlockers.length > result.report.top_3_fixes.length); + assert.equal((html.match(/

Dimension blocker<\/h4>/g) ?? []).length, dimensionBlockers.length * 2); + for (const blocker of dimensionBlockers) { + assert.match(html, new RegExp(escapeHtml(blocker.title))); + for (const evidence of blocker.evidence) { + assert.match(html, new RegExp(escapeHtml(evidence.detail))); + } + } let priorIndex = -1; for (const fix of result.report.top_3_fixes) { const currentIndex = html.indexOf(escapeHtml(fix.title)); @@ -132,5 +198,6 @@ test("publishing contract fails closed on GitHub visibility before rendering or assert.match(contract, /if \[ "\$VISIBILITY" != "PUBLIC" \]/); assert.match(contract, /lookup failure, or any visibility\s+other than `PUBLIC` stops/); assert.ok(visibilityGate < render, "visibility must be checked before rendering"); + assert.match(contract, /render-report\.mjs "\$OUT\/atr-1\.json" --out-dir "\$OUT" --hosted/); assert.ok(visibilityGate < upload, "visibility must be checked before upload"); });