syntaxai/tdd.md · main · src / b51_render_projects.ts
// c51 (projects) — body builders for /projects, /projects/new,
// /projects/:owner/:repo. Imports chrome helpers from c51_render_layout.
import type { ProjectRow } from "./a31_project_config.ts";
import { PROJECT_CONFIG_PATH } from "./a31_project_config.ts";
import { escape } from "./b51_render_layout.ts";
const projectListRow = (p: ProjectRow): string => {
const slug = `${p.repoOwner}/${p.repoName}`;
const display = p.displayName ?? slug;
const team = p.team ? ` <span class="muted">· ${escape(p.team)}</span>` : "";
const branches = p.trackedBranches.map((b) => `\`${b}\``).join(", ");
const runner = p.testRunner === "none" ? "trace-only" : p.testRunner;
return `| [${escape(display)}](/projects/${p.repoOwner}/${p.repoName}) ${team} | ${branches} | ${runner} |`;
};
export const projectsLandingMd = (projects: ProjectRow[]): string => {
const rows = projects.length === 0
? `| _no projects yet — [register one](/projects/new)_ | | |`
: projects.map(projectListRow).join("\n");
return `# projects
> Real repos that opted in to tdd.md scoring. Each project drops \`${PROJECT_CONFIG_PATH}\` at its root, registers here, and from then on its commits on tracked branches get judged structurally — red-fails, green-passes, no test-deletion, no regression. The aggregated scores feed [the reports](/reports).
## tracked
| project | branches | runner |
|---|---|---|
${rows}
## register a repo
[Register a project →](/projects/new) — paste a public GitHub URL; tdd.md fetches \`${PROJECT_CONFIG_PATH}\` from the default branch and onboards it.
## the config file
Drop \`${PROJECT_CONFIG_PATH}\` at the root of your repo's default branch:
\`\`\`json
{
"version": 1,
"test_runner": "none",
"tracked_branches": ["main"],
"display_name": "API Gateway",
"team": "platform"
}
\`\`\`
- **\`test_runner\`** — \`"none"\` for trace-mode (commit-discipline only, language-agnostic). \`"bun"\` will run the test suite once the sandbox-runner ships.
- **\`tracked_branches\`** — pushes to these branches get scored. Defaults to \`["main"]\`.
- **\`display_name\`** / **\`team\`** — optional, only used in the reporting UI.
## what comes next
Registration just stores the project. Per-commit judging (the part that produces score data for the reports) lands in the next sliver — until then the [report pages](/reports) keep showing the demo dataset.
[← back to tdd.md](/) · [the reports](/reports)
`;
};
export const projectRegisterMd = (
viewer: string | null,
prefilled?: string,
errorMessage?: string,
): string => {
if (!viewer) {
return `# register a project
> You need to sign in before registering a project. We use your GitHub identity to record who onboarded the repo.
[ sign in with github → ](/auth/github/start)
[← all projects](/projects)
`;
}
const error = errorMessage
? `<div class="project-form-error"><strong>Couldn't register that repo:</strong><br>${escape(errorMessage)}</div>`
: "";
const value = prefilled ? ` value="${escape(prefilled)}"` : "";
return `# register a project
> Paste a public GitHub URL. tdd.md fetches \`${PROJECT_CONFIG_PATH}\` from its default branch, validates it, and onboards the repo. Re-register the same repo to refresh the config.
${error}
<form method="post" action="/projects/new" class="project-form">
<label for="repo-url">Repository URL or <code>owner/name</code></label>
<input id="repo-url" name="repo" type="text" required
placeholder="https://github.com/owner/name"
autocomplete="off" autocapitalize="off" autocorrect="off"${value} />
<button type="submit">Register</button>
</form>
> Signed in as <code>${escape(viewer)}</code>. Don't have \`${PROJECT_CONFIG_PATH}\` yet? [See the format on /projects](/projects#the-config-file).
[← all projects](/projects)
`;
};
export const projectDetailMd = (p: ProjectRow): string => {
const display = p.displayName ?? `${p.repoOwner}/${p.repoName}`;
const registeredAt = new Date(p.registeredAt).toISOString().slice(0, 10);
const branches = p.trackedBranches.map((b) => `\`${b}\``).join(", ");
const runnerNote = p.testRunner === "none"
? "Trace-mode — judging looks at commit phase tags, test-count drift, and refactor stability. No test execution."
: "Bun runner — test suite executes in a sandbox at every tracked-branch commit. (Sandbox-runner ships in the next sliver; meanwhile this falls back to trace-mode.)";
return `# ${escape(display)}
> [${escape(p.repoOwner)}/${escape(p.repoName)}](https://github.com/${p.repoOwner}/${p.repoName}) · registered by [${escape(p.registeredBy)}](/agents/${p.registeredBy}) on ${registeredAt}.
## config
| key | value |
|---|---|
| test_runner | \`${p.testRunner}\` |
| tracked_branches | ${branches} |
| display_name | ${p.displayName ? `\`${escape(p.displayName)}\`` : "_(none)_"} |
| team | ${p.team ? `\`${escape(p.team)}\`` : "_(none)_"} |
| status | \`${p.status}\` |
${runnerNote}
## scored commits
> _No commits judged yet._ The webhook ingest + judging pipeline lands in the next sliver — once it does, scored commits for tracked branches will appear here grouped by agent.
## refresh
Push an updated \`${PROJECT_CONFIG_PATH}\` to your default branch and [re-register](/projects/new?repo=${encodeURIComponent(`${p.repoOwner}/${p.repoName}`)}) to pick up the new config.
[← all projects](/projects)
`;
};