Bundle
dsh-workspace-hygiene
A DeepSeek Harness plugin that actively assesses artifact value and keeps agent workspaces organized, auditable, and recoverable.
- Source
- taoshi1999
- stars
- 3 stars
- License
- MIT
- Updated
- Updated 7 days ago
Readme
# dsh-workspace-hygiene
English | [中文](README.zh.md)
`dsh-workspace-hygiene` is a small, auditable DeepSeek Harness bundle for a problem that appears in almost every long-running agent task: useful source files stay in the workspace, while temporary logs, checkpoints, scratch exports, and failed intermediate results keep accumulating around them.
Files do not usually consume model tokens merely by existing on disk. The cost
appears when an agent repeatedly globs, greps, lists directories, and rereads
its own intermediate output: the search space expands, more irrelevant content
enters context, reasoning noise rises, and token cost, latency, and performance
degrade. Harness workspace budgets help with access and safety, but they do not
answer which files still deserve to exist, what they should be called, or how
they should be organized.
This plugin treats the workspace as an evolving information space rather than a
passive file bucket. It continuously asks which artifacts are valuable, what
their purpose and lifecycle are, and whether a recommendation should be kept,
reviewed, archived, or deleted. The assessment is explicit and explainable;
the action is separate and can be reviewed, overridden, or run in an explicitly
autonomous mode. The before/after example below shows why that lifecycle matters:
## A concrete before/after workspace
The four static screenshots below use a generic software-project workspace
rather than any particular repository. At the beginning, `src/`, `tests/`, and
the final report are mixed with logs, scratch files, failed patches, and
duplicate exports from many agent turns. Without workspace governance, every
later search has to re-decide which file is current and which files are only
process residue.
With the plugin enabled, the workspace first receives an explainable value
assessment. The plugin creates a lightweight `workspace-artifacts/` metadata
catalog that records each source file's original directory and name, purpose,
value assessment, suggested name/location, and handling recommendation. Catalog
entries point to source files; they do not contain copies of those files. The
existing workspace layout therefore remains unchanged by default. The four
screenshots show the messy workspace, the resulting search noise, the value
assessment, and a high-signal workspace with an index rather than duplicated
artifacts.
| Comparison | Without the plugin | With the plugin |
| --- | --- | --- |
| File value | Deliverables and process residue are mixed | Retain, archive, review, and to-delete metadata entries are explicit |
| Layout and names | Temporary names and duplicate copies are ambiguous | Entries record original and suggested paths/names; source files stay put by default |
| Follow-up work | The agent repeatedly globs, greps, and lists to find the “current” result | A stable structure makes evidence quick for people and agents to locate |
| Risk | Cleanup tends to be one-off and hard to audit | Assessment and action are separate; deletion and catalog state stay auditable |
```mermaid
flowchart LR
A[agent writes artifacts] --> B[debounced idle scan]
B --> C[value assessment]
C --> D[update workspace-artifacts metadata catalog]
D --> E{operating mode}
E -- manual --> F[human reviews and confirms]
E -- autonomous --> G[apply policy automatically]
F --> G
G --> H[move/rename/delete source files when allowed]
H --> I[synchronize entries and audit]
```
### 1. Unmanaged workspace

### 2. Search noise without governance

### 3. Explainable value review

### 4. Organized high-signal workspace

These four screenshots use the same small, synthetic project workspace so the
lifecycle is visible without exposing anyone's files. The first two show the
unmanaged workspace and the search noise it creates; the last two show an
explainable value review and the indexed result. Static screenshots can be read
one by one and cited in documentation, code review, or research material.
`workspace-artifacts/` is a metadata index only; source files remain at their
original paths unless an operator confirms an organization action or explicitly
enables autonomous mode.
The “destination” paths shown in screenshot 3 are metadata suggestions used to
make a future organization legible; they do not mean that source files are
copied into `workspace-artifacts/`.
```text
# before: mixed lifecycle and names
project/
├── src/
├── tests/
├── tmp/run-001.log
├── tmp/model-output-final-2.txt
├── scratch/patch-attempt-07.diff
└── outputs/export-draft-copy.csv
# after: original files stay in place; a lightweight metadata catalog is added
project/
├── src/
├── tests/
├── reports/final/benchmark-2026-09-02.md
└── workspace-artifacts/
├── retained/
│ └── reports-final--a13f2c9e.json
├── intermediate/
│ └── tmp-run-001--5dd1a04c.json
├── review/
│ └── scratch-patch-07--91d0b4af.json
└── to-delete/
└── outputs-export-copy--7f3e1138.json
~/.dsh/workspace-hygiene-archive/<run>/
├── notes.tmp
├── run-002.log
└── export-draft-copy.csv
```
The “after” tree is a representative comparison, not a promise that every
file is moved automatically. The four directories contain small JSON metadata
records; each record at minimum names the source `relativePath`, `purpose`, and
`recommendation`, and may include value reasons, a suggested name/path, last
observation, and a fingerprint. Idle maintenance incrementally updates the
catalog: new files are registered, changed assessments move an entry between
groups, and missing sources are marked stale for review. Stale records are
removed only by an explicit pruning operation. The catalog itself is
excluded from candidate scanning.
In manual mode, scanning and catalog updates do not mutate source files. After
an operator confirms an organization action, the plugin may move or rename a
source file according to the entry. After an operator confirms a `to-delete`
entry, it rechecks the path, protection status, age, and fingerprint, deletes
the source file, and removes the corresponding metadata entry. If the source
changed, moved, or could not be deleted, the entry remains and records the
reason. State and audit records preserve what happened, and an optional
quarantine can keep recoverable copies.
The existing workspace layout is not globally rewritten by default. Suggested
directories and names are governance information first. In manual mode, only
an explicitly confirmed organization request turns a source suggestion into a
physical move or rename. During idle maintenance, physical organization
requires both `autonomousMode: true` and
`valuePolicy.organization.moveFiles: true`; autonomous mode by itself may
quarantine or delete according to policy, but does not imply source moves.
Symlinks, path escapes, source trees, `.env*` files, credential-looking names,
key/certificate extensions (`.pem`, `.key`, `.p12`, and related formats), common
known lockfile names, and Git-tracked files are protected by default.
### `workspace-artifacts/` is a metadata catalog, not a file repository
The four subdirectories express the current governance state of each entry;
they are not mandatory physical destinations for source files:
```text
workspace-artifacts/
├── retained/ # source should stay; record path and retention rationale
├── intermediate/ # useful evidence; record suggested organization
├── review/ # insufficient evidence; await an operator decision
└── to-delete/ # proposed deletion; confirm, delete source, then remove entry
```
For example, `review/scratch-patch-07--91d0b4af.json` can contain only metadata,
not the patch body:
```json
{
"relativePath": "scratch/patch-attempt-07.diff",
"directory": "scratch",
"name": "patch-attempt-07.diff",
"purpose": "Failed attempt retained as possible regression evidence",
"recommendation": "review",
"suggestedName": "2026-09-02-patch-attempt-07.diff",
"suggestedPath": "workspace-artifacts/review/2026-09-02-patch-attempt-07.diff",
"suggestedSourcePath": "scratch/2026-09-02-patch-attempt-07.diff",
"status": "pending"
}
```
`suggestedPath` names the metadata entry's catalog location. When physical
organization is enabled, `suggestedSourcePath` (also exposed as
`physicalSuggestedPath`) names the possible source-file destination. Neither
field means that source bytes are copied into the catalog.
These small, readable, diffable records are the first search layer for people
and agents. When the actual evidence is needed, the caller follows
`relativePath` to read it. This preserves the project's existing directory
semantics while keeping repeated searches out of the context hot path.
Entry lifecycle is intentionally small:
| Entry status | Meaning | Are source bytes changed? |
| --- | --- | --- |
| `pending` | Assessment and recommendation recorded; awaiting a decision | No |
| `stale` | The source is missing, moved, or has a changed fingerprint; reassess it | No |
| `confirmed` / `applied` | An operator or autonomous policy approved and completed the action | According to the approved action |
> Key contract: `workspace-artifacts/` manages information about files, not the
> files themselves. Unless an organization action is enabled, the plugin does
> not force project files into these four directories.
## Why this plugin exists
The plugin makes an explicit value assessment for each manageable artifact. It
combines hard protection rules, operator keep/discard rules, and bounded
metadata signals to classify artifacts as valuable, intermediate, disposable,
or uncertain. For each item it recommends retain, archive, delete, or review,
shows the score, signals, and reasons behind that recommendation, and proposes
a predictable directory and filename. The result is first written as a small
metadata record under `workspace-artifacts/`; the record points to the source
file and never stores a second copy of it. The operator can override any
recommendation, review the evidence, and choose whether to organize or delete
the source.
The decision vocabulary is intentionally small:
| Value class | Default action | Meaning |
| --- | --- | --- |
| `protected` | `retain` | Source, credentials, tracked files, and other hard-protected paths; never mutated |
| `valuable` | `retain` | Explicitly kept or strongly supported by value signals |
| `intermediate` | `archive` | Useful evidence; the catalog records an archive/organization suggestion while the source stays put by default |
| `disposable` | `delete` | Strong evidence that the artifact has no continuing value; the entry waits in `to-delete/` for confirmation |
| `uncertain` | `review` | Insufficient evidence for an automatic decision |
Explicit retain/discard rules take precedence over heuristic signals. A plan
records the rule or signal that won, and
`decisions: {"relative/path":"retain|archive|delete|review"}` lets an operator
override the recommendation without changing the scanner. Decision paths and
actions are validated before a plan is written; malformed overrides fail closed
instead of silently falling back to a different action. A rule can promote a
safe ordinary file into the actionable inventory, and an explicit per-path
decision can select such a file even when its name does not look temporary.
Value assessment can still explain files outside `managedRoots`, but only
eligible files inside those roots are put into an action plan. This keeps the
scope of mutations narrow while allowing durable outputs to remain visible in
the catalog. `workspace-artifacts/` is metadata space and is never recursively
registered as its own source.
## What it provides
The bundle registers six model-facing tools:
| Tool | Purpose | Mutates files? |
| --- | --- | --- |
| `workspace_hygiene_scan` | Bounded inventory of probable intermediate artifacts; returns existing/pending catalog metadata | No source-file mutation |
| `workspace_hygiene_plan` | Create a deterministic plan and short-lived plan token | No |
| `workspace_hygiene_apply` | Revalidate and execute reviewed organization, quarantine, or deletion actions, then synchronize catalog entries | Yes; confirmation required unless autonomous mode is enabled |
| `workspace_hygiene_restore` | Restore one prior run record after hash/path checks; `dryRun` previews pending entries | Yes, reversible |
| `workspace_hygiene_status` | Compact status, latest run, and disk accounting | No |
| `workspace_hygiene_explain` | Explain why a path is protected, ordinary, or a candidate | No |
Automatic maintenance is intentionally quiet: on an `agent/status: idle` transition the plugin can schedule a debounced scan through `agent.runMaintenance()`. It does not inject every scan into the next prompt unless `notifyAgent: true` is configured, because maintenance should not consume context by default.
In scan results, `candidateCount` is the actionable count inside
`managedRoots`; `counts.candidate` is a diagnostic classification total and
may include candidates outside that scope.
The value policy keeps the metadata catalog small and predictable. These
directories contain entries, not source files:
```text
workspace-artifacts/
├── retained/ # source should stay; record path and retention rationale
├── intermediate/ # useful evidence; record suggested archive/organization
├── review/ # insufficient evidence; await an operator decision
└── to-delete/ # proposed deletion; confirm, delete source, then remove entry
```
Scan/plan/explain results include the original directory and name, purpose,
recommendation, and optional suggested directory/name. Idle maintenance reads
the catalog and incrementally updates it: new files receive entries, changed
assessments move entries between groups, and missing sources are marked stale
for review. Stale records are removed only by an explicit pruning operation.
The catalog is not a backup; optional quarantine and audit records are
managed separately.
### Manual and autonomous operation
Manual operation is the default. Idle maintenance only refreshes the catalog;
physical moves, renames, and deletions require a reviewed plan and explicit
confirmation. In particular, confirming a `to-delete` entry causes the plugin
to recheck its path, protection status, age, and fingerprint, delete the source
file, and remove the matching catalog entry. If the source changed, moved, or
could not be deleted, the entry remains with an explanatory status.
For unattended experiments, set `autonomousMode: true` explicitly:
```json
{
"autonomousMode": true,
"autoScan": true,
"managedRoots": ["tmp", "scratch", "outputs/staging"],
"valuePolicy": {
"allowDelete": true,
"organization": {
"root": "workspace-artifacts",
"moveFiles": true,
"fileRoot": ""
}
}
}
```
In `autonomousMode`, each maintenance cycle may apply eligible archive/delete
decisions without a per-run confirmation; physical organization of retain/review
suggestions is attempted only when `valuePolicy.organization.moveFiles: true`.
Review-only suggestions stay pending for a person when physical organization is
disabled; enabling autonomy does not silently turn uncertainty into approval.
`to-delete` entries are handled according to the delete policy and removed only
after successful deletion. Autonomy does not remove boundaries: protected paths,
symlink/path checks, managed roots, age/file/byte budgets, plan fingerprints,
and the delete permission still apply. Use an isolated profile, a narrow
allow-list, and a tested recovery or backup policy. `autoScan` controls catalog
refresh and is on by default without changing source files; `autoArchive` is a
legacy switch for automatic quarantine of selected archive/delete decisions.
It does not enable autonomous mode or physical source organization. Both
mutation switches (`autoArchive` and `autonomousMode`) are off by default, and
without autonomous mode the original workspace layout is not globally
rewritten.
## Install as a Harness bundle
The package follows the current DeepSeek Harness bundle contract (`type: module`, `main`, and `dsh.bundle.patch`). In a profile:
Compatibility baseline (verified 2026-09-02): DeepSeek Harness
`dsh-v0.1.2-alpha.4`, commit
`4e84901e6471b79ec0338099867ebb4606d12bb5` (developer preview), and Node
`22.19+` or `24+`. The tool schema is also compatible with the earlier
`dsh-v0.1.2-alpha.3` baseline. Other previews may require small adapter
changes; pin the Harness/profile versions used for an experiment.
`@deepseek-ai/dsh-tools` is declared as an optional peer dependency on
purpose: a real Harness profile supplies the host tool registry, while the
standalone scanner/CLI can be used without installing a second copy of that
runtime. A headless context without `ctx.tools` can still use the core library,
but it will not expose model-facing tools.
```sh
dsh plugin --profile research add ./dsh-workspace-hygiene
dsh --profile research --dump-config
```
Remove it from a profile with:
```sh
dsh plugin --profile research remove dsh-workspace-hygiene
```
Restart the profile after adding, removing, or updating a bundle so the
composed Cordis layer is rebuilt.
For a Git checkout, replace `<owner>` with the eventual repository owner, pin a
commit, and review the source before installation:
```sh
dsh plugin --profile research add github:<owner>/dsh-workspace-hygiene#<commit>
```
Use an isolated profile while evaluating a new plugin. Installed plugins execute in the Harness host process and inherit its permissions; normal model-tool approval prompts do **not** sandbox plugin code.
## Configuration
The inserted row enables bounded idle scans and catalog synchronization with
safe mutation defaults. It does not set `managedRoots`, so the default scope is
the whole workspace subject to the protected-path rules; `autonomousMode` and
`autoArchive` remain off. For a shared repository, override the complete row in
your profile's `cordis.patch.yml` and set a narrow `managedRoots` list:
```yaml
- id: workspace-hygiene
name: dsh-workspace-hygiene
config:
enabled: true
catalogEnabled: true
catalogRoot: workspace-artifacts
# Keep inspection narrow. These are workspace-relative.
managedRoots:
- .dsh/ephemeral
- .dsh/tmp
- tmp
- scratch
- outputs/.staging
minAgeHours: 24
maxScanFiles: 5000
maxArchiveFiles: 20
maxArchiveBytes: 104857600
autoScan: true
autoArchive: false
autonomousMode: false
notifyAgent: false
# Relative means inside the workspace root (not the state directory).
stateDir: .dsh-hygiene
# Prefer an external location for long-lived quarantine data.
archiveRoot: ~/.dsh/workspace-hygiene-archive
```
This replaces the bundle's existing `workspace-hygiene` row by id. Use
`- insert:` only when adding a new, differently named row; inserting the same
`id` would create a duplicate loader entry.
Important safety knobs:
- `managedRoots` narrows the scan. An omitted list scans the workspace subject to the protected-path rules; for a shared repository, set it explicitly to a small list such as `.dsh/ephemeral`, `.dsh/tmp`, `tmp`, and `scratch`.
- An explicitly listed managed root may live under a normally protected hidden/configuration directory (for example `.dsh/ephemeral`); repository metadata, dependency trees, and `.env*` names remain non-overridable.
- `minAgeHours` prevents a file that was just produced by the current turn from being selected.
- `maxArchiveFiles` and `maxArchiveBytes` bound one apply operation.
- Built-in credential/key/certificate protections cannot be removed by replacing
`protectedNames` or `protectedExtensions`; those options only add project-
specific rules.
- `.hygieneignore` contains simple path/glob exclusions; a trailing `/` covers
a directory and its descendants. Negation patterns are intentionally
unsupported. The file must be a regular file inside the workspace and is
capped at 256 KiB; symlinks and oversized files fail closed. Reports expose
only a bounded prefix of the pattern list.
- Keep `allowPathOverride` false unless an operator deliberately wants model
calls to select a different workspace; enabling it removes the session-root
boundary and should be paired with an isolated profile.
- `allowHiddenCandidates` only relaxes the hidden-name guard; hard-protected
metadata, dependency, `.env*`, and built-in protected names remain protected.
- Relative `archiveRoot` values stay inside the workspace; use an absolute path
or `~` when an external archive is intentional. It must not be the workspace
itself, an ancestor that contains the workspace, or a directory nested inside
a managed root; these layouts would overlap the source tree and are rejected.
Quarantine records include source, destination, size, hash, timestamp, and
status. Catalog entries only contain metadata and handling recommendations;
source files are never copied into `workspace-artifacts/`.
- `autonomousMode` is the full-autonomy switch and is off by default. When
enabled, idle maintenance may execute eligible archive/delete decisions
without a per-run confirmation. Autonomous physical organization additionally
requires `valuePolicy.organization.moveFiles: true`. Hard protections,
managed roots, age/file/byte budgets, and fingerprint checks still apply. Use
it only in an isolated profile after observing manual plans.
- `autoScan` controls periodic catalog refresh and does not by itself move or
delete source files.
- `autoArchive` is a legacy idle-maintenance switch for automatically
quarantining selected archive/delete recommendations. It is off by default,
does not enable `autonomousMode`, and does not perform physical source
organization.
- `catalogEnabled` controls persistence of the metadata catalog; `catalogRoot`
selects its workspace-relative root (default `workspace-artifacts`). If the
top-level value is omitted, `valuePolicy.organization.root` can select the
root (the organization `root` is the catalog namespace, not a source-file
destination). `retainDirectory`, `archiveDirectory`, `reviewDirectory`, and
`deleteDirectory` name catalog groups, not mandatory physical destinations
for source files. The catalog root must not overlap the state or archive
roots.
- `valuePolicy.organization.moveFiles` controls whether accepted organization
suggestions become physical moves/renames during autonomous idle
maintenance; it is off by default. A reviewed, explicit `apply --organize`
request may still perform a move after confirmation even when this flag is
false. `fileRoot` can set a workspace-relative physical organization root.
Protected paths and symlink boundaries still win.
## Example: a generic project workspace
For a project where an Agent produces temporary logs, scratch files, and
staged exports, keep durable deliverables in explicitly named directories such
as `reports/final/`, `outputs/release/`, and `artifacts/published/`. Put
disposable material in clearly scoped roots such as `tmp/`, `scratch/`,
`outputs/`, and `artifacts/staging/`. This gives a person and a later Agent a
predictable place to look before deciding what to retain, relocate, or delete
after review. Start with a narrow, review-only profile:
```json
{
"catalogEnabled": true,
"catalogRoot": "workspace-artifacts",
"managedRoots": ["tmp", "scratch", "outputs", "artifacts/staging"],
"minAgeHours": 24,
"maxArchiveFiles": 20,
"maxArchiveBytes": 104857600,
"autoScan": true,
"autoArchive": false,
"autonomousMode": false,
"archiveRoot": "~/.dsh/workspace-hygiene-archive",
"valuePolicy": {
"retainPatterns": [
"reports/final/**",
"outputs/release/**",
"artifacts/published/**"
],
"discardPatterns": [
"tmp/**",
"scratch/**",
"artifacts/staging/**"
],
"defaultAction": "review",
"allowDelete": false,
"organization": {
"root": "workspace-artifacts",
"retainDirectory": "retained",
"archiveDirectory": "intermediate",
"reviewDirectory": "review",
"deleteDirectory": "to-delete",
"namingTemplate": "{date}-{stem}{ext}",
"moveFiles": false,
"fileRoot": ""
}
}
}
```
Useful `valuePolicy` controls are deliberately small: `retainPatterns`,
`discardPatterns`, `retainNames`/`discardNames`, and
`retainExtensions`/`discardExtensions` express project knowledge;
`retainThreshold`, `archiveThreshold`, and `deleteThreshold` tune the score
bands; `defaultAction` chooses the fallback for uncertain items; and
`organization` controls the catalog root, groups, and naming suggestions. Set
`valuePolicy.enabled: false` to pause heuristic value decisions; non-protected
items then become explicit `review` entries instead of being silently treated
as disposable. `allowDelete` enables the policy side of deletion; manual mode
still requires user confirmation, while `autonomousMode` permits maintenance
cycles to execute according to policy.
Copy `examples/workspace-hygiene.config.json` to `workspace-hygiene.json` in the
directory where you run the CLI (or pass its full path with `--config`).
Inspect the inventory and catalog first, then review the plan before moving or
deleting anything in manual mode:
```sh
node bin/dsh-workspace-hygiene.mjs scan /path/to/workspace \
--config workspace-hygiene.json
node bin/dsh-workspace-hygiene.mjs plan /path/to/workspace \
--config workspace-hygiene.json
# Optional reviewed overrides (JSON object keyed by workspace-relative path).
node bin/dsh-workspace-hygiene.mjs plan /path/to/workspace \
--config workspace-hygiene.json \
--actions retain,delete \
--include-retain \
--decisions '{"tmp/old.log":"delete"}'
```
Copy `examples/workspace.hygieneignore` to the workspace's `.hygieneignore`
and adjust the keep patterns after reviewing a plan. The `valuePolicy` section
labels explicit keep/discard patterns, reports a retain/archive/delete/review
recommendation, and writes a catalog entry containing the source path, name,
purpose, and suggested handling. This starter keeps automatic deletion off;
delete recommendations remain in `to-delete/` until a person confirms them.
After reviewing a plan, an operator may override actions with `decisions` and
choose whether to execute physical organization. Durable final/release/published
outputs remain visible to the value assessment and are retained by the
`retainPatterns`; human review notes are kept by the ignore template, while
staging and scratch material remain eligible for review. Source, tests,
documentation, tracked files, dependency trees, and credentials remain
protected by default.
## Safety model
1. Scan and catalog synchronization are bounded by traversal depth, file count,
and result size. Catalog writes contain metadata only; source bytes are never
copied into `workspace-artifacts/`.
2. A plan records the policy fingerprint and metadata/hash for each selected
candidate; scan and report surfaces remain bounded projections.
3. Manual apply requires the plan token and explicit confirmation; it re-stats
and re-hashes every source before moving, renaming, or deleting it.
4. Destinations are collision-safe and never overwrite an existing file; the
original layout is not globally rewritten by default.
5. After a confirmed `to-delete` action, the plugin deletes the source file and
removes its matching catalog entry. If any check fails, the entry remains and
records the reason. Restore and quarantine records remain available when that
execution profile enables them; `manifest.jsonl` is append-only audit state.
6. `autonomousMode` can skip per-run confirmation for eligible maintenance
actions, but cannot bypass hard protections, managed roots, age/file/byte
budgets, fingerprints, or the explicit deletion policy. It is off by default;
there is no unconfirmed autonomous purge unless the operator opts in.
## Development
The first release is plain ESM JavaScript so a Git or tarball install does not require an install-time build hook:
```sh
npm test
npm pack --dry-run
```
The same core can be tried without starting Harness. The scan command also
refreshes the metadata catalog and does not copy or move source files:
```sh
npm install
node bin/dsh-workspace-hygiene.mjs scan /path/to/workspace
node bin/dsh-workspace-hygiene.mjs plan /path/to/workspace
# Review the returned planId/token before using --confirm.
node bin/dsh-workspace-hygiene.mjs apply /path/to/workspace \
--plan PLAN_ID --token TOKEN --dry-run
```
Use `--confirm` only after reviewing the plan in manual mode. Manual apply
requires the exact `planId`, the complete (not merely the displayed
`tokenHint`) token, and explicit confirmation. Confirming a `to-delete` entry
deletes its source and removes the catalog record. The `restore` command accepts
a run id from a completed apply and requires `--confirm` before moving archived
files. For an unattended experiment, set `autonomousMode: true` in the profile
or JSON passed with `--config`; there is no CLI flag that enables autonomy, and
`autoScan` alone never grants it.
The implementation is split into small, auditable modules:
```text
index.js Harness entry point and lifecycle wiring
cordis.patch.yml Installable bundle layer
src/config.js Schemastery surface and normalized defaults
src/policy.js Managed-root, protection, and policy fingerprints
src/value-policy.js Value classification and organization suggestions
src/catalog.js Metadata-only workspace-artifacts catalog and lifecycle sync
src/scan.js Bounded deterministic scanner
src/transaction.js Plan/apply/restore and JSONL manifest
src/tools.js Model-facing tool definitions and compact renderers
src/report.js Bounded Markdown/JSON report for human review
test/ Node's built-in test suite
examples/ Profile, generic workspace config, and ignore examples
```
## Troubleshooting
- `CONFIRMATION_REQUIRED`: for apply, call again with the exact `planId`, the
complete token returned by `workspace_hygiene_plan`, and `confirm: true`; for
restore, pass the `runId` and `confirm: true`. A `tokenHint` is only a display
aid and cannot authorize a mutation.
- `PLAN_STALE_POLICY`, `SOURCE_CHANGED`, or `PLAN_EXPIRED`: create a fresh
plan; the old plan is intentionally not reused.
- No candidates: check `managedRoots`, `minAgeHours`, `.hygieneignore`, and
the protected-directory/name defaults. `workspace_hygiene_explain` gives a
path-level reason.
- `SYMLINK_REJECTED` or `PATH_ESCAPE`: inspect the path and state/archive
roots manually. The plugin fails closed and does not follow links.
- `IGNORE_FILE_REJECTED` or `IGNORE_FILE_TOO_LARGE`: replace
`.hygieneignore` with a regular, in-workspace file no larger than 256 KiB;
the scanner will not consume a linked or oversized policy file.
More background is in [`docs/community-research.md`](docs/community-research.md)
and the bilingual [`research agenda`](docs/research-agenda.md).
## Research roadmap
This project is an engineering baseline for future research. It already makes
explicit value and organization decisions, records the evidence behind each
decision, and lets operators calibrate the policy with rules and per-file
overrides. That makes the first release useful in practice while leaving a
clear seam for stronger models and evaluations. Promising directions include:
- provenance-aware cleanup from `tools/result` and file-operation traces;
- artifact utility estimation and lifecycle-aware retention policies;
- measuring token cost, disk growth, scan latency, and human comprehensibility;
- controlled benchmarks comparing unmanaged and managed workspaces;
- reversible summarization of logs/checkpoints instead of simple relocation;
- policy learning with hard safety constraints and human-in-the-loop review.
DeepSeek Harness is still in developer preview. APIs and package names may change; pin versions and keep an isolated profile for experiments.
## License
MIT. See [LICENSE](LICENSE).
For security issues, do not paste secrets or private paths into a public issue;
add a private contact channel when the project is published.
Install
dsh plugin --profile web add github:taoshi1999/dsh-workspace-hygiene#23776dad05c849897374dc6637432ce73c50adac
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-workspace-hygiene from the hub