Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:

- name: Upload test results to Codecov
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
uses: codecov/test-results-action@v5
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
Expand Down
323 changes: 323 additions & 0 deletions .scratch/stale-docs-audit.md

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions .scratch/stale-docs-cleanup-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Stale Docs Cleanup Report

## Section A — Issue tracker

Selected: **GitHub Issues**
Evidence: `git remote` → `github.com/thedavidweng/OpenLoop`, `gh` available
Workflow: `gh issue create`

## Deleted

| File | Reason |
| --- | --- |
| `docs/archive/plans/Development_Plan.md` | Completed plan — all phases marked done, v0.1.0 shipped |
| `docs/archive/plans/2026-04-24-openkara-shell-parity-design.md` | Archive debris |
| `docs/archive/plans/2026-04-24-openkara-shell-parity-implementation.md` | Archive debris |
| `docs/archive/plans/2026-04-25-commercial-generation-workspace-design.md` | Archive debris |
| `docs/archive/plans/2026-04-25-commercial-generation-workspace-implementation.md` | Archive debris |
| `docs/plans/2026-04-28-acestep-feature-benefits.md` | "Status: Completed on 2026-04-29" |
| `docs/plans/2026-04-28-ui-review.md` | "Status: Completed on 2026-04-29" |
| `docs/plans/2026-05-13-cli-backend-vnext.md` | All milestones [x], implementation done |
| `docs/plans/2026-05-14-v1-readiness-master-plan.md` | Massive completed master plan (713 lines) |
| `docs/plans/2026-05-17-complexity-hotspots-optimization.md` | "Plan only" — work covered by issue #59 |
| `docs/plans/2026-05-30-cli-ux-fixes.md` | Plan, issue #57 CLOSED |
| `docs/2026-06-09-cli-clap-refactoring.md` | Untracked handoff doc; clap files already exist in tree |
| `docs/2026-06-10-issue-plans-and-evals.md` | Untracked 700-line plan; all 29 items have matching GitHub issues |

## Rewritten

| File | Change |
| --- | --- |
| `docs/implementation-status.md` | Removed "Planned after v0.2" list; replaced with link to GitHub issues |
| `docs/superpowers/specs/2026-06-07-testing-strategy-design.md` | Removed "Status: Draft"; trimmed stale problem statement |
| `docs/specs/2026-05-04-openloop-cli-design.md` | Removed completed Phase 6 checklist items (update implementation-status.md, update PRD, update CONTEXT.md) |
| `README.md` | "Planned" section: replaced feature list with issue tracker link. "Known Limitations": replaced forward-looking Repaint language with issue link |

## Kept

| File | Reason |
| --- | --- |
| `docs/adr/0001–0004` | Durable architectural decision records |
| `docs/agents/domain.md` | Agent navigation guide (current, stable) |
| `docs/agents/issue-tracker.md` | GitHub workflow reference |
| `docs/agents/triage-labels.md` | Label mapping contract |
| `docs/cli.md` | Human CLI usage guide |
| `docs/OpenLoop_PRD.md` | Product requirements (marked "Implemented") |
| `docs/privacy.md` | Privacy policy (bilingual, current) |
| `docs/release.md` | Release checklist and Gatekeeper guide |
| `docs/release-notes/v0.1.0.md, v0.2.0.md, v0.2.1.md` | Human-readable release history |
| `docs/specs/event-schema.md` | Frozen NDJSON event contract |
| `docs/specs/2026-05-04-openloop-cli-design.md` | CLI design spec (rewritten to remove stale checklists) |
| `docs/testing.md` | QA procedures, manual checklist, regression triggers |

## Remaining decisions

- `docs/implementation-status.md` duplicates README content. Consider deleting and keeping README as single source of truth.
- `docs/OpenLoop_PRD.md` says "主要实现对象: Codex / Coding Agent / Human Developer" — agent-facing language in a human doc. Low priority to fix.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Thanks for your interest in contributing.
```bash
git clone https://github.com/thedavidweng/OpenLoop.git
cd OpenLoop
mise install # install tools pinned in mise.toml
pnpm install
```

Expand Down
Binary file added docs/screenshots/01-initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tools]
node = "24"
pnpm = "11.5.2"
38 changes: 38 additions & 0 deletions scripts/screenshot.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { chromium } from 'playwright';

const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1440, height: 920 },
deviceScaleFactor: 2,
});
const page = await context.newPage();

await page.goto('http://localhost:1420', { waitUntil: 'networkidle', timeout: 15000 });
await page.waitForTimeout(2000);

await page.screenshot({ path: 'docs/screenshots/01-initial.png', fullPage: false });
console.log('Screenshot 1: initial page');

const title = await page.title();
console.log('Page title:', title);

const bodyText = await page.textContent('body');
console.log('Body text (first 800 chars):', bodyText?.substring(0, 800));

const buttons = await page.$$('button');
console.log('Button count:', buttons.length);
for (const btn of buttons.slice(0, 15)) {
const text = await btn.textContent();
const visible = await btn.isVisible();
if (visible && text?.trim()) console.log(' Button:', text.trim());
}

const inputs = await page.$$('input, textarea');
console.log('Input count:', inputs.length);
for (const inp of inputs.slice(0, 10)) {
const placeholder = await inp.getAttribute('placeholder');
const visible = await inp.isVisible();
if (visible) console.log(' Input placeholder:', placeholder);
}

await browser.close();
115 changes: 115 additions & 0 deletions scripts/validate-readme.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env node
/**
* validate-readme.mjs
* Validates README.md, README_CN.md, CSP ADR, and v1 plan for Issue #69.
*
* Behavioral criteria:
* 1. README.md license badge says Apache-2.0, not MIT
* 2. README_CN.md license badge says Apache-2.0, not MIT
* 3. README.md contains status line with v0.1 Alpha
* 4. README_CN.md contains status line with v0.1 Alpha (Chinese)
* 5. README_CN.md has Release badge
* 6. CSP ADR references Tauri v2 security docs, not v1
* 7. v1 plan tasks 1.2.4, 1.5.2, 1.5.3 are checked off
* 8. License section text in both READMEs says Apache-2.0
*/

import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, "..");

function readFile(rel) {
return readFileSync(resolve(ROOT, rel), "utf-8");
}

const checks = [];
let passCount = 0;
let failCount = 0;

function check(name, ok, detail) {
if (ok) {
passCount++;
checks.push(` PASS ${name}`);
} else {
failCount++;
checks.push(` FAIL ${name}${detail ? " — " + detail : ""}`);
}
}

// Load files
const readme = readFile("README.md");
const readmeCN = readFile("README_CN.md");
const cspAdr = readFile("docs/adr/0003-content-security-policy.md");
const v1Plan = readFile("docs/plans/2026-05-14-v1-readiness-master-plan.md");

// 1. README.md license badge says Apache-2.0
check(
"1. README.md license badge is Apache-2.0",
/License-MIT/.test(readme) === false && /Apache--2\.0|License-Apache/.test(readme) === true,
"badge still says MIT or missing Apache-2.0"
);

// 2. README_CN.md license badge says Apache-2.0
check(
"2. README_CN.md license badge is Apache-2.0",
/License-MIT/.test(readmeCN) === false && /Apache--2\.0|License-Apache/.test(readmeCN) === true,
"badge still says MIT or missing Apache-2.0"
);

// 3. README.md contains status line with v0.1 Alpha
check(
"3. README.md has v0.1 Alpha status line",
/v0\.1\s+Alpha/i.test(readme),
"no 'v0.1 Alpha' found"
);

// 4. README_CN.md contains status line with v0.1 Alpha (Chinese)
check(
"4. README_CN.md has v0.1 Alpha status line",
/v0\.1\s+Alpha/i.test(readmeCN),
"no 'v0.1 Alpha' found in Chinese README"
);

// 5. README_CN.md has Release badge (shield.io badge in the header badge block)
const cnBadgeBlock = readmeCN.split("</div>")[0]; // everything before first closing div
check(
"5. README_CN.md has Release badge",
/img\.shields\.io.*release/i.test(cnBadgeBlock) || /\[!\[Release\]/i.test(cnBadgeBlock),
"missing Release badge in header badge block"
);

// 6. CSP ADR references Tauri v2 security docs
check(
"6. CSP ADR references Tauri v2 (not v1)",
/tauri\.app\/v1/.test(cspAdr) === false && /tauri\.app.*v2|v2\.tauri\.app/.test(cspAdr) === true,
"still references tauri.app/v1"
);

// 7. v1 plan tasks 1.2.4, 1.5.2, 1.5.3 are checked off
const task124 = /\[x\]\s*1\.2\.4/.test(v1Plan);
const task152 = /\[x\]\s*1\.5\.2/.test(v1Plan);
const task153 = /\[x\]\s*1\.5\.3/.test(v1Plan);
check("7a. v1 plan task 1.2.4 is checked", task124);
check("7b. v1 plan task 1.5.2 is checked", task152);
check("7c. v1 plan task 1.5.3 is checked", task153);

// 8. License section text says Apache-2.0
check(
"8a. README.md License section says Apache-2.0",
/## License[\s\S]*?Apache-2\.0/i.test(readme) && /## License[\s\S]*?MIT/.test(readme) === false,
"License section still mentions MIT"
);
check(
"8b. README_CN.md License section says Apache-2.0",
/## 许可证[\s\S]*?Apache-2\.0/i.test(readmeCN) && /## 许可证[\s\S]*?MIT/.test(readmeCN) === false,
"License section still mentions MIT"
);

// Report
console.log("\n=== Issue #69 Validation ===\n");
for (const line of checks) console.log(line);
console.log(`\n ${passCount} passed, ${failCount} failed\n`);
process.exit(failCount > 0 ? 1 : 0);
84 changes: 84 additions & 0 deletions scripts/validate-release-notes.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env node

/**
* validate-release-notes — Check that DMG release notes contain a Gatekeeper
* bypass section with both methods (right-click Open and xattr -cr) and a
* Homebrew alternative.
*
* Exit codes:
* 0 — all checked release notes pass
* 1 — one or more checks failed
*/

import { readFileSync, readdirSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_ROOT = resolve(__dirname, "..");
const RELEASE_NOTES_DIR = resolve(PROJECT_ROOT, "docs/release-notes");

const CHECKS = [
{
id: "gatekeeper-heading",
desc: "Gatekeeper section heading present",
test: (content) => /#{1,4}\s*[Gg]atekeeper/.test(content),
},
{
id: "right-click-open",
desc: "Right-click Open bypass method present",
test: (content) =>
/[Rr]ight-?[Cc]lick.*[Oo]pen|[Rr]ight-?[Cc]lick.*[Oo]pen/.test(content),
},
{
id: "xattr-cr",
desc: "xattr -cr bypass method present",
test: (content) => /xattr\s+-cr/.test(content),
},
{
id: "homebrew-mention",
desc: "Homebrew alternative mentioned",
test: (content) => /[Hh]omebrew|brew\s+(tap|install|upgrade)/.test(content),
},
];

function main() {
let files;
try {
files = readdirSync(RELEASE_NOTES_DIR).filter((f) => f.endsWith(".md"));
} catch {
console.error(`✗ Cannot read directory: ${RELEASE_NOTES_DIR}`);
process.exit(1);
}

if (files.length === 0) {
console.log("⚠ No release note files found — nothing to check.");
process.exit(0);
}

let failures = 0;

for (const file of files.sort()) {
const filePath = resolve(RELEASE_NOTES_DIR, file);
const content = readFileSync(filePath, "utf-8");

for (const check of CHECKS) {
if (!check.test(content)) {
console.error(`✗ ${file}: ${check.desc} [${check.id}]`);
failures++;
}
}
}

if (failures === 0) {
console.log(
`✓ ${files.length} release note(s) pass all Gatekeeper checks.`
);
process.exit(0);
}

console.error(`\n${failures} check(s) failed.`);
process.exit(1);
}

main();
1 change: 1 addition & 0 deletions src-tauri/migrations/005_history_indexes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE INDEX IF NOT EXISTS idx_generations_created_at ON generations (created_at DESC);
Loading