chore: add agent harness (state, verification, lifecycle tracking)

Adds the missing harness subsystems so future agent sessions can start,
stay in scope, verify work, and resume reliably:

- feature_list.json: local mirror of active/next-up work (GitHub Issues
  on Project #6 remains the source of truth for the full backlog)
- progress.md / session-handoff.md / archive/2026-07.md: session state,
  restart point, and monthly archive of finished work
- init.sh: install + lint + test + build verification entrypoint
- AGENTS.md: added Startup Workflow, Definition of Done, Stay in Scope,
  and End of Session sections (existing agent-tier content kept as-is)
- CLAUDE.md: added a short Agent Workflow section routing to the above

Validated with the firstsun-harness audit script: 100/100 across all
five subsystems (instructions, state, verification, scope, lifecycle).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYCTyZw7gUmJ7oh1VTmAqh
This commit is contained in:
ClaudiaFang 2026-07-13 12:27:42 +00:00
parent 28f4f8efd0
commit 597989b53c
7 changed files with 239 additions and 0 deletions

View file

@ -1,5 +1,31 @@
# Project Agent Design & Hierarchy (Synchronized with Skills)
## Startup Workflow
Before writing code:
- Read `feature_list.json` (active/next-up work — GitHub Issues on `firstsun-dev/git-files-sync`, Project #6, is the real source of truth; re-sync stale entries) and `progress.md` (what's currently open).
- Read `session-handoff.md` for the previous session's exact stopping point.
- Run `./init.sh` to confirm a clean, green baseline before editing.
## Definition of Done
A feature is done only when all of the following hold, with evidence recorded (command + result) in `progress.md`:
- `npx eslint .` — 0 errors
- `npm run build` — passes (tsc + Obsidian 1.11.0 compat typecheck + esbuild)
- `npx vitest run` — passes
- Manual verification in Obsidian, when the change has a runtime UI surface
## Stay in Scope
- One feature at a time: pick a single `feature_list.json` entry and finish it (with recorded evidence) before starting the next.
- Don't expand scope mid-task — file a new GitHub issue via the `firstsun-pm` skill instead of quietly bundling unrelated work.
## End of Session
Before ending a session:
- Overwrite `session-handoff.md` (don't append) with the new stopping point so the repo stays restartable.
- Move finished items from `progress.md` into `archive/YYYY-MM.md` (current month) — next steps in `progress.md` should read as a short list, not a changelog.
## Agent Tiers
### 1. High-Tier (Red/Orange Group)

View file

@ -2,6 +2,15 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Agent Workflow
- **Startup**: read `feature_list.json` (active/next-up work; GitHub Issues on `firstsun-dev/git-files-sync`, Project #6, is the actual source of truth — re-sync before trusting stale entries) and `progress.md` (what's open right now), then `session-handoff.md` for the previous session's exact stopping point.
- **Before editing**: run `./init.sh` (installs deps, then lint + test + build) to confirm you're starting from a green baseline.
- **Definition of done**: `npx eslint .` has 0 errors, `npm run build` passes (includes the Obsidian 1.11.0 compat typecheck), and `npx vitest run` passes, *and* evidence of that run is recorded (one line: command + result) in `progress.md` or the PR description — not just claimed.
- **Scope**: work one `feature_list.json` entry at a time; don't start the next until the current one's evidence is recorded.
- **End of session**: overwrite `session-handoff.md` with the new stopping point, move finished items from `progress.md` into `archive/YYYY-MM.md` (current month).
- Issue/PR conventions (Conventional Commits titles, Project #6 fields, English-only for this public plugin repo) are defined in the `firstsun-pm` skill, not duplicated here.
## Development Commands
- Build: `npm run build` (runs type check and esbuild in production mode)
- Dev: `npm run dev` (builds in watch mode using esbuild)

9
archive/2026-07.md Normal file
View file

@ -0,0 +1,9 @@
# Progress Archive — 2026-07
One archive file per calendar month. Each entry is one line: feature id/name +
commit hash. Debugging narrative and design discussion belong in the commit
message, not here. Start a new `archive/YYYY-MM.md` file when the month rolls
over — don't let a single archive file grow without bound either.
- feat-001: Project Setup verified clean (npm install/lint/build/test) (commit 28f4f8e)
- feat-002: Settings UX bundle implemented — conflict modal resize + tabs (#42), connection status badge (#41), local ignore patterns (#40); 254 tests pass, lint/build clean (commit 28f4f8e, branch claude/settings-ux-improvements-260713)

46
feature_list.json Normal file
View file

@ -0,0 +1,46 @@
{
"_note": "GitHub Issues (firstsun-dev/git-files-sync, Project #6) is the source of truth for the full backlog and priority/estimate fields. This file mirrors only the active feature and the next few candidates so an agent session has a local, offline checkpoint — sync it against `gh issue list --repo firstsun-dev/git-files-sync --state open` at the start of a session rather than treating it as authoritative.",
"features": [
{
"id": "feat-001",
"name": "Project Setup",
"description": "Confirm the project can install dependencies, run verification, and start from a clean checkout",
"dependencies": [],
"status": "done",
"evidence": "Commit 28f4f8e - npm install/lint/build/test all pass from a clean checkout"
},
{
"id": "feat-002",
"name": "Settings UX bundle (issues #40, #41, #42)",
"description": "Local ignore-pattern sync setting (#40), persistent connection status badge in settings (#41), and resized/tabbed conflict resolution modal (#42)",
"dependencies": ["feat-001"],
"status": "in-review",
"evidence": "Commit 28f4f8e on branch claude/settings-ux-improvements-260713 - lint/build/test pass (254 tests); pushed to origin, PR not yet opened"
},
{
"id": "feat-003",
"name": "fix: symbolic link pull fails (issue #33)",
"description": "Bug report with screenshot; needs repro/investigation before a fix can be scoped",
"dependencies": [],
"status": "not-started",
"evidence": ""
},
{
"id": "feat-004",
"name": "fix: resolve sonarqube issues (issue #45)",
"description": "Review current SonarQube findings and fix code smells/bugs/security hotspots where applicable",
"dependencies": [],
"status": "not-started",
"evidence": ""
},
{
"id": "feat-005",
"name": "feat(settings): folder picker for root path / vault folder (issue #48)",
"description": "Searchable folder suggester for the Root path and Vault folder settings fields, replacing free-text entry",
"dependencies": [],
"status": "not-started",
"evidence": ""
}
],
"_evidenceStyle": "Keep evidence to one line: commit hash + short pointer (e.g. 'Commit abc1234 - added X, tests pass'). Debugging narrative and design discussion belong in the commit message, not this file."
}

24
init.sh Executable file
View file

@ -0,0 +1,24 @@
#!/bin/bash
set -e
echo "=== Harness Initialization ==="
echo "=== npm install ==="
npm install
echo "=== npm run lint ==="
npm run lint
echo "=== npm test ==="
npm test
echo "=== npm run build ==="
npm run build
echo "=== Verification Complete ==="
echo ""
echo "Next steps:"
echo "1. Read feature_list.json to see current feature state"
echo "2. Pick ONE unfinished feature to work on"
echo "3. Implement only that feature"
echo "4. Re-run verification before claiming done"

66
progress.md Normal file
View file

@ -0,0 +1,66 @@
# Session Progress Log
<!--
CLEANUP CADENCE: this file tracks only what's still open. When a feature
finishes, move its narrative to archive/YYYY-MM.md (current month) as a
one-line entry (name + commit hash) and remove it from here. Archive once
this file passes ~80 lines — "What's Done" is a snapshot, not a permanent
changelog.
-->
Completed work is archived in [archive/](./archive/), one file per calendar month — this file only tracks what's still open.
## Current State
**Last Updated:** 2026-07-13 11:40
**Session ID:** session_01YYCTyZw7gUmJ7oh1VTmAqh
**Active Feature:** feat-002 - Settings UX bundle (issues #40, #41, #42)
## Status
### What's Done
- [x] feat-001 - Project Setup verified (see archive/2026-07.md)
- [x] feat-002 implementation - all three sub-features coded, tested, linted, built (see archive/2026-07.md)
### What's In Progress
- [ ] feat-002 - Open a PR for branch `claude/settings-ux-improvements-260713` and get it merged
- Details: commit 28f4f8e is pushed to origin; no PR opened yet
- Blockers: none, just needs `gh pr create`
### What's Next
1. Open PR for `claude/settings-ux-improvements-260713`, close issues #40/#41/#42 on merge
2. Pick up feat-003 (issue #33, symlink pull fails) — needs repro investigation first
3. Re-sync this file's backlog entries against `gh issue list --repo firstsun-dev/git-files-sync --state open` since new issues may have been filed (e.g. #48 folder picker was added mid-session)
## Blockers / Risks
- None currently.
## Decisions Made
- **Discarded a stale local WIP for Obsidian 1.12.x compatibility**: origin/main already shipped this via PR #46 (`applyDestructiveStyle`, `typecheck-compat.mjs`) with a different mechanism. Local main was fast-forwarded to origin/main and the new features (#40/#41/#42) were manually re-applied on top of the correct base rather than merged via `git stash pop` (which would have conflicted/duplicated the compat layer).
- **feat-002 bundles three issues in one commit**: #40/#41/#42 touch overlapping files (`src/settings.ts`, `styles.css`, `tests/setup.ts`) closely enough that splitting into three atomic commits wasn't worth the risk of an intermediate broken state.
## Files Modified This Session
- `src/settings.ts` - connection status badge (#41), ignore patterns setting (#40)
- `src/ui/SyncConflictModal.ts` - Diff/Local/Remote tab switcher (#42)
- `src/logic/gitignore-manager.ts`, `src/main.ts` - local ignore pattern matching (#40)
- `styles.css` - badge + modal + tab styles
- `tests/setup.ts`, `tests/ui/setup-dom.ts` - expanded Obsidian mocks (TextAreaComponent, removeClass, configDir, etc.)
- `tests/logic/gitignore-manager.test.ts`, `tests/logic/sync-manager*.test.ts`, `tests/ui/SyncConflictModal.test.ts`, `tests/ui/SettingsConnectionStatus.test.ts` - new/updated tests
## Evidence of Completion
- [x] Tests pass: `npx vitest run` → 254/254 passed
- [x] Type check clean: `npm run build` (tsc + Obsidian 1.11.0 compat typecheck + esbuild) → clean
- [x] Lint clean: `npx eslint .` → 0 errors
- [ ] Manual verification in Obsidian: not done (no Obsidian instance available in this environment)
## Notes for Next Session
- Branch `claude/settings-ux-improvements-260713` is pushed but has no PR yet — check with the user before opening one (they were asked and hadn't confirmed as of session end).
- `feature_list.json`'s backlog (feat-003..005) is a snapshot from 2026-07-13; re-check `gh issue list` before trusting it.

59
session-handoff.md Normal file
View file

@ -0,0 +1,59 @@
# Session Handoff
<!--
OVERWRITE, don't append: this file describes only the most recent session.
Rewrite it at end of session; older handoffs live in git history, and completed
work belongs in archive/YYYY-MM.md. If this file grows past ~80 lines, it is
accumulating history instead of handing off.
-->
## Current Objective
- Goal: Implement issues #40 (local ignore patterns), #41 (settings connection status badge), #42 (resize conflict modal)
- Current status: Implemented, tested, linted, built clean; committed and pushed. PR not yet opened (pending user confirmation).
- Branch / commit: `claude/settings-ux-improvements-260713` @ 28f4f8e
## Completed This Session
- [x] #42 - Resized conflict modal (`min(1100px,92vw) x min(85vh,800px)`, flex layout, removed 280px content cap, added Diff/Local/Remote tab switcher on narrow screens)
- [x] #41 - Persistent connection status badge in settings tab (Checking/Connected/Not connected), 800ms debounce on token/branch/URL/owner/repo edits, updates in place (no focus-stealing re-render)
- [x] #40 - "Ignore patterns" setting (multi-line, .gitignore-style), applied in `GitignoreManager.isIgnored()` additively alongside remote/local `.gitignore`
- [x] Filed issue #48 (folder picker for root path / vault folder settings) at user's request mid-session
- [x] Rebased local `main` onto `origin/main` to adopt the already-shipped Obsidian 1.12.x compat work (PR #46), discarding a stale duplicate local WIP, then re-applied the above three features cleanly on top
## Verification Evidence
| Check | Command | Result | Notes |
|---|---|---|---|
| Lint | `npx eslint .` | 0 errors | Repo-wide, no exceptions |
| Type check + compat | `npm run build` | Pass | Includes `typecheck-compat.mjs` against Obsidian 1.11.0 |
| Tests | `npx vitest run` | 254/254 passed | 17 test files |
| Manual (in Obsidian) | — | Not done | No Obsidian instance available in this environment |
## Files Changed
- `src/settings.ts`, `src/ui/SyncConflictModal.ts`, `src/logic/gitignore-manager.ts`, `src/main.ts`, `styles.css`
- `tests/setup.ts`, `tests/ui/setup-dom.ts` (expanded Obsidian test mocks)
- `tests/logic/gitignore-manager.test.ts`, `tests/logic/sync-manager.test.ts`, `tests/logic/sync-manager-mapping.test.ts`
- `tests/ui/SyncConflictModal.test.ts`, `tests/ui/SettingsConnectionStatus.test.ts` (new)
## Decisions Made
- Bundled #40/#41/#42 into a single commit rather than three, since they touch overlapping files closely enough that atomic per-issue commits risked an intermediate broken build.
- Discarded the local uncommitted 1.12.x-compat WIP (user confirmed it was superseded by origin/main's PR #46) rather than trying to merge two different compat mechanisms.
## Blockers / Risks
- None blocking. Open item: PR for the pushed branch hasn't been created — user was asked "要接著幫你開 PR 嗎?" and the session moved to harness setup before they answered.
## Next Session Startup
1. Read `CLAUDE.md`.
2. Read `feature_list.json` and `progress.md`.
3. Review this handoff.
4. Run `./init.sh` before editing.
5. Check whether the user wants a PR opened for `claude/settings-ux-improvements-260713` before starting new work.
## Recommended Next Step
- Ask the user whether to open the PR for the pushed branch; if yes, use `gh pr create` per the repo's PR conventions (see `CLAUDE.md` / firstsun-pm workflow) and reference issues #40/#41/#42 so they auto-close on merge.