Skip to content

Commit 692f415

Browse files
authored
Merge pull request #69 from U1oSun/feat/harness-component-snapshot-v1
feat(analysis): add harness component snapshots
2 parents 6bbcfd1 + d053275 commit 692f415

23 files changed

Lines changed: 2104 additions & 24 deletions

File tree

docs/ARCHITECTURE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ the target journey, contract, evidence, governance, and DX-measurement system.
105105
its bounded human-review integrity chain: packet binding, episode/delivery
106106
normalization, and review application. Non-facade consumers import its public
107107
`index.mjs`; historical flat paths remain compatibility entrypoints.
108+
- `scripts/harness-component-snapshot/` owns the versioned, read-only Qoder
109+
project component snapshot, validation, bounded diff, and non-authorizing
110+
rollback-reference contracts. It composes only allowlisted public
111+
`agent-customize` project collections, keeps its direct CLI capability-local,
112+
and does not claim runtime activation or mutation authority.
108113
- `schemas/` is a target owner, not yet created, for versioned public runtime contracts consumed by multiple repo surfaces or packaged hosts; see the directory-structure ADR for adoption criteria. Capability-private schemas stay under `scripts/<capability>/`.
109114

110115
## AI Directory Routing
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Freeze Project Harness Components for Qoder
2+
3+
## Traceability
4+
5+
- Spec ID: `harness-component-snapshot-v1`
6+
- Roadmap: `LC-02`
7+
- Status: Implemented
8+
- AI involvement: Codex (GPT 5.6 Sol), implementation; Codex (GPT 5.6 Terra), independent review
9+
10+
## Intent
11+
12+
Give maintainers and later loop capabilities a deterministic, privacy-safe way
13+
to identify the exact project-owned Qoder Rules, Skills, Hooks, Commands, and
14+
Workflows that existed at one observation boundary. A versioned snapshot and
15+
bounded diff must distinguish component identity, content revision, and
16+
activation evidence so later evaluation or intervention work can bind to facts
17+
without treating configured presence as observed use.
18+
19+
This is the first read-only `LC-02` slice. It establishes one complete host
20+
contract before generalizing across providers.
21+
22+
## Acceptance Scenarios
23+
24+
- **HCS-AC-1 (bounded Qoder inventory):** Given a workspace, snapshot creation
25+
uses Qoder's public project inventory for Rules, Skills, Hooks, and Commands,
26+
plus only `.qoder/workflows/` and `.agents/workflows/` for Workflows. User,
27+
plugin, inherited, runtime-cache, Memory, MCP, and subagent assets are out of
28+
scope and are neither collected nor completeness gates. The Qoder inventory
29+
request explicitly allowlists only Rules, Skills, Hooks, and Commands. Every
30+
supported kind reports `observed` coverage even when its result is empty.
31+
Malformed Hook JSON, user-home/project-root aliasing, unsupported
32+
symlinks, or a collector depth/count/probe overflow fails closed instead of
33+
silently claiming complete observation.
34+
- **HCS-AC-2 (stable component identity):** Every component receives a stable,
35+
provider-, scope-, and population-qualified ID from `qoder`, `project`, a
36+
privacy-safe population reference, its kind, and a normalized
37+
workspace-relative route. Hook identity adds a digest of its event, matcher,
38+
and handler type plus an ordinal only for duplicate routing identities;
39+
declaration position belongs to revision evidence. Collector/component output
40+
ordering and host path separators do not affect identity. The safe default
41+
binds population to a hash of the canonical workspace boundary; callers use
42+
an explicit opaque population key when the same project must retain identity
43+
across relocation, drive letters, or operating systems.
44+
- **HCS-AC-3 (separate evidence dimensions):** Content revision, identity, and
45+
activation are independent fields. File content, a bounded Skill tree, or a
46+
bounded hook configuration plus its workspace script determines a SHA-256
47+
revision. Hook command/script content and declaration order do not enter its
48+
routing identity; a routing tuple change is an identity change. Activation
49+
remains explicitly `unknown` with `unavailable` runtime evidence. File-backed
50+
provenance is `observed` and never claims execution.
51+
- **HCS-AC-4 (privacy-safe frozen contract):** `HarnessComponentSnapshotV1` is
52+
deeply frozen, canonically ordered, versioned, content-addressed, and contains
53+
no raw component content, command text, secret values, or absolute workspace
54+
or user-home paths. Symlink or relative-path escapes fail closed. Evidence
55+
reads reuse one canonical workspace boundary scoped to the current snapshot;
56+
they do not retain caller paths in a process-lifetime cache.
57+
- **HCS-AC-5 (minimal graph):** Each component has one typed `declared-in`
58+
relationship to its privacy-safe workspace artifact reference. Relationship
59+
validation rejects unknown component IDs, absolute targets, and mismatched
60+
provenance instead of inferring semantic dependencies.
61+
- **HCS-AC-6 (tamper-safe validation):** Validation recomputes component
62+
identity, relationship integrity, rollback references, ordering, and the
63+
snapshot digest. Stale or edited snapshots fail with stable diagnostic codes.
64+
- **HCS-AC-7 (bounded diff):** Two valid snapshots from the same provider and
65+
scope and exact population reference produce deterministic `added`, `removed`, `changed`, and `unchanged`
66+
counts. Revision changes are named `content`; route identity changes appear
67+
as one removal and one addition. Activation and provenance remain fixed,
68+
explicit evidence dimensions in this v1 producer and therefore cannot be
69+
reported as changed without a future contract revision. Returned entries
70+
obey an explicit bounded limit and report truncation. Different populations
71+
fail closed.
72+
- **HCS-AC-8 (non-authorizing rollback reference):** Every component exposes a
73+
parseable reference binding provider, scope, population-qualified component
74+
ID, and revision.
75+
Resolution requires a valid matching snapshot and returns
76+
`mutationAuthorized: false`; it does not read prior content or execute a
77+
restore.
78+
- **HCS-AC-9 (public automation surface):** A capability-owned public
79+
`index.mjs` exports create, validate, diff, parse, and rollback-resolution
80+
functions. A strict `cli.mjs` exposes `create`, optional opaque
81+
`--population-key`, `validate`, `diff`, and `resolve` with parser-safe JSON
82+
stdout and usage/runtime failures on stderr. Runtime diagnostics expose a
83+
stable code without echoing caller-selected paths or untrusted snapshot
84+
fields. Public API failures use stable sanitized messages without appending
85+
filesystem or parser error details. Global and leaf help remain help-only
86+
human text and do not inspect a workspace.
87+
- **HCS-AC-10 (portable evidence):** Fixtures cover Windows, macOS, and Linux
88+
route forms, stable input ordering, provider/scope isolation, secret and home
89+
sentinels, hook activation, tampering, bounded diff states, and rollback
90+
resolution. Package verification and the full test suite remain green apart
91+
from already-known environment-specific failures recorded as evidence.
92+
93+
## Non-goals
94+
95+
- Supporting providers other than Qoder, user/global assets, installed plugins,
96+
inherited assets, MCPs, Memories, or subagents.
97+
- Inferring runtime activation, observed use, causal impact, semantic
98+
dependencies, or cross-component invocation from configured presence.
99+
- Storing raw component bodies, hook commands, environment values, absolute
100+
homes, transcripts, prompts, or private Memory content.
101+
- Creating a graph database, trace runner, scheduler, evaluator, automatic
102+
reviewer, mutation planner, apply command, or rollback executor.
103+
- Registering a new root `better-harness` command. The v1 slice remains an
104+
atomic capability-owned direct CLI; root command naming, audience, and
105+
discovery integration are an explicit follow-up decision.
106+
- Reusing `plugin-lifecycle` identity, contract, runtime, or host-support APIs.
107+
A future adapter may project accepted lifecycle observations into this
108+
component contract only after their evidence semantics are reviewed.
109+
110+
## Plan and Tasks
111+
112+
1. Add `scripts/harness-component-snapshot/` as the atomic owner, with a
113+
versioned capability-private contract, canonical hashing, path safety,
114+
snapshot assembly, bounded diff, rollback-reference resolution, public
115+
exports, and a direct CLI.
116+
2. Compose Qoder Rules, Skills, Hooks, and Commands through an explicit project
117+
collection allowlist on `scripts/agent-customize/index.mjs`. Add a narrow
118+
Qoder workflow collector locally because the existing cross-provider
119+
practices inventory groups unrelated host workflow roots and has no
120+
workflow-only public API.
121+
3. Use one snapshot-scoped canonical workspace read context, workspace-relative
122+
artifact references, population references, and SHA-256 digests only. Keep
123+
activation unavailable and provenance file-backed; make unsupported or
124+
escaped evidence a hard failure.
125+
4. Add fixture-driven contract and CLI tests. Test route normalization as a pure
126+
boundary so Windows/macOS/Linux behavior is reproducible on every CI host.
127+
5. Run focused tests, documentation-link validation, package verification, and
128+
the full suite. Perform a Change Traceability Review Readiness Check before
129+
handoff.
130+
131+
### Boundary with the plugin lifecycle control plane
132+
133+
The merged plugin lifecycle control plane describes installation, discovery,
134+
enablement, version relation, and read-only mutation plans for the Better
135+
Harness plugin itself across multiple hosts. This spec instead snapshots
136+
project-owned Qoder harness assets. It does not reuse plugin identity, expose
137+
host-support profiles, claim lifecycle/runtime state, or modify the root CLI
138+
registry. The only intentional conceptual seam is a future explicit adapter
139+
from accepted host evidence to the component snapshot contract.
140+
141+
## Test and Review Evidence
142+
143+
- **HCS-AC-1..5:** `node --test test/harness-component-snapshot.test.mjs`
144+
validates inventory filtering, IDs, revisions, graph edges, privacy, deep
145+
freezing, ordering, and path fixtures.
146+
- **HCS-AC-6..8:** The same focused test injects stale digests, edited fields,
147+
unknown relationships, population mismatch, all diff states, limits, and
148+
rollback-reference mismatches.
149+
- **HCS-AC-9:** `node --test test/harness-component-snapshot-cli.test.mjs`
150+
verifies help, strict parsing, JSON stdout, path-safe stderr failures, and all
151+
four direct CLI operations. Contract tests also verify that malformed Hook
152+
input cannot expose parser details, caller paths, or private sentinels through
153+
the public API error message.
154+
- **HCS-AC-10:** `node --test test/doc-link-graph.test.mjs`,
155+
`npm run pack:verify`, and `npm test` provide documentation, distribution, and
156+
repository-wide regression evidence.
157+
- **Observed evidence (2026-08-05):** On the rebased final tree, the focused
158+
contract/CLI run passed 21 tests with 0 failures and 0 skips; documentation-
159+
link validation passed 6/6. The repository-wide run passed 1,288, failed 0,
160+
and skipped 1 existing platform-specific case. Package verification passed
161+
with 458 npm and 480 runtime-zip entries. An independent Terra contract and
162+
adversarial review ended with no P1/P2 findings after the Qoder collection
163+
allowlist, path-safe CLI diagnostics, tilde-home alias guard, architecture
164+
routing, and Windows drive-relative route fixes were applied. Copilot review
165+
follow-up additionally replaced per-read workspace-root resolution with a
166+
snapshot-scoped boundary and removed parser/filesystem details from public
167+
Hook configuration errors.
168+
- **Risk — privacy:** A route or source field could expose an absolute home.
169+
Mitigation: accept only normalized workspace-relative routes and serialize
170+
no inventory path fields.
171+
- **Risk — false activation:** Configured assets could be presented as used.
172+
Mitigation: freeze `activation.state = unknown` and
173+
`activation.evidenceState = unavailable` in this version.
174+
- **Risk — unstable hook identity:** Hook array reordering changes registration
175+
positions but retains each distinct routing identity; revisions change because
176+
order can affect execution. Editing the event, matcher, or handler type is an
177+
identity change. Inserting or removing indistinguishable duplicate routing
178+
declarations may renumber only those duplicates. Tests separately cover
179+
irrelevant collector ordering and meaningful hook declaration reordering.
180+
- **Risk — project relocation:** Default population identity deliberately binds
181+
to the canonical workspace boundary so unrelated projects with identical
182+
files cannot cross-diff or resolve rollback references. Repositories that
183+
relocate or compare across operating systems must provide the same opaque
184+
population key; only its domain-separated digest enters artifacts.
185+
- **Risk — incomplete rollback:** A privacy-safe snapshot cannot itself restore
186+
omitted source bytes. The v1 reference is resolvable and non-authorizing;
187+
executable restore remains gated on a future content-store or Git provenance
188+
contract.
189+
- **Risk — boundedness:** Snapshot assembly rejects more than 20,000 component
190+
descriptors before starting evidence reads and processes accepted populations
191+
with eight workers. All evidence workers share one canonical workspace root
192+
scoped to that snapshot, so an input near the limit cannot create unbounded
193+
root-resolution I/O, process-lifetime cache growth, or in-flight content
194+
buffers.
195+
- **Risk — root CLI drift:** The merged lifecycle work established a richer root
196+
command registry after this slice was designed. Mitigation: keep the v1
197+
entrypoint capability-local and leave root registry/help untouched until a
198+
separately reviewed command name, audience, and discovery contract is chosen.

scripts/agent-customize/inventory.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { collectProviderInventory } from "./providers/index.mjs";
88

99
export async function collectAgentCustomizeInventory(options = {}) {
1010
const provider = String(options.provider ?? "cursor").toLowerCase();
11+
if (options.projectCollections !== undefined && provider !== "qoder") {
12+
throw new TypeError("projectCollections is supported only for the Qoder provider");
13+
}
1114
return collectProviderInventory(provider, {
1215
...options,
1316
...normalizedHostHomeOptions(options, provider),

0 commit comments

Comments
 (0)