fix: use UK English spelling throughout
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
7950b56988
commit
ef5437b207
25 changed files with 1683 additions and 29 deletions
122
.core/CLAUDE.md
Normal file
122
.core/CLAUDE.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
`forge.lthn.ai/core/go-io` is the **mandatory I/O abstraction layer** for the CoreGO ecosystem. All data access — files, configs, journals, state — MUST go through the `io.Medium` interface. Never use raw `os`, `filepath`, or `ioutil` calls.
|
||||
|
||||
### The Premise
|
||||
|
||||
**The directory you start your binary in becomes the immutable root.** `io.NewSandboxed(".")` (or the CWD at launch) defines the filesystem boundary — everything the process sees is relative to that root. This is the SASE containment model.
|
||||
|
||||
If you need a top-level system process (root at `/`), you literally run it from `/` — but that should only be for internal services never publicly exposed. Any user-facing or agent-facing process runs sandboxed to its project directory.
|
||||
|
||||
Swap the Medium and the same code runs against S3, SQLite, a Borg DataNode, or a runc rootFS — the binary doesn't know or care. This is what makes LEM model execution safe: the runner is an apartment with walls, not an open box.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
core go test # Run all tests
|
||||
core go test --run Name # Single test
|
||||
core go fmt # Format
|
||||
core go lint # Lint
|
||||
core go vet # Vet
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Interface
|
||||
|
||||
`io.Medium` — 18 methods: Read, Write, EnsureDir, IsFile, FileGet, FileSet, Delete, DeleteAll, Rename, List, Stat, Open, Create, Append, ReadStream, WriteStream, Exists, IsDir.
|
||||
|
||||
```go
|
||||
// Sandboxed to a project directory
|
||||
m, _ := io.NewSandboxed("/home/user/projects/example.com")
|
||||
m.Write("config/app.yaml", content) // writes inside sandbox
|
||||
m.Read("../../../etc/passwd") // blocked — escape detected
|
||||
|
||||
// Unsandboxed system access (use sparingly)
|
||||
io.Local.Read("/etc/hostname")
|
||||
|
||||
// Copy between any two mediums
|
||||
io.Copy(s3Medium, "backup.tar", localMedium, "restore/backup.tar")
|
||||
```
|
||||
|
||||
### Backends (8 implementations)
|
||||
|
||||
| Package | Backend | Use Case |
|
||||
|---------|---------|----------|
|
||||
| `local` | Local filesystem | Default, sandboxed path validation, symlink escape detection |
|
||||
| `s3` | AWS S3 | Cloud storage, prefix-scoped |
|
||||
| `sqlite` | SQLite (WAL mode) | Embedded database storage |
|
||||
| `node` | In-memory + tar | Borg DataNode port, also implements `fs.FS`/`fs.ReadFileFS`/`fs.ReadDirFS` |
|
||||
| `datanode` | Borg DataNode | Thread-safe (RWMutex) in-memory, snapshot/restore via tar |
|
||||
| `store` | SQLite KV store | Group-namespaced key-value with Go template rendering |
|
||||
| `workspace` | Core service | Encrypted workspaces, SHA-256 IDs, PGP keypairs |
|
||||
| `MockMedium` | In-memory map | Testing — no filesystem needed |
|
||||
|
||||
`store.Medium` maps filesystem paths as `group/key` — first path segment is the group, remainder is the key. `List("")` returns groups as directories.
|
||||
|
||||
### Sigil Transformation Framework (`sigil/`)
|
||||
|
||||
Composable data transformations applied in chains:
|
||||
|
||||
| Sigil | Purpose |
|
||||
|-------|---------|
|
||||
| `ReverseSigil` | Byte reversal (symmetric) |
|
||||
| `HexSigil` | Base16 encoding |
|
||||
| `Base64Sigil` | Base64 encoding |
|
||||
| `GzipSigil` | Compression |
|
||||
| `JSONSigil` | JSON formatting |
|
||||
| `HashSigil` | Cryptographic hashing (SHA-256, SHA-512, BLAKE2, etc.) |
|
||||
| `ChaChaPolySigil` | XChaCha20-Poly1305 encryption with pre-obfuscation |
|
||||
|
||||
Pre-obfuscation strategies: `XORObfuscator`, `ShuffleMaskObfuscator`.
|
||||
|
||||
```go
|
||||
// Encrypt then compress
|
||||
encrypted, _ := sigil.Transmute(data, []sigil.Sigil{chacha, gzip})
|
||||
// Decompress then decrypt (reverse order)
|
||||
plain, _ := sigil.Untransmute(encrypted, []sigil.Sigil{chacha, gzip})
|
||||
```
|
||||
|
||||
Sigils can be created by name via `sigil.NewSigil("hex")`, `sigil.NewSigil("sha256")`, etc.
|
||||
|
||||
### Security
|
||||
|
||||
- `local.Medium.validatePath()` follows symlinks component-by-component, checks each resolved path is still under root
|
||||
- Sandbox escape attempts log `[SECURITY]` to stderr with timestamp, root, attempted path, username
|
||||
- `Delete` and `DeleteAll` refuse `/` and `$HOME`
|
||||
- `io.NewSandboxed(root)` enforces containment — this is the SASE boundary
|
||||
|
||||
## Conventions
|
||||
|
||||
### Import Aliasing
|
||||
|
||||
Standard `io` is always aliased to avoid collision with this package:
|
||||
```go
|
||||
goio "io"
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
coreio "forge.lthn.ai/core/go-io" // when imported from subpackages
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
All errors use `coreerr.E("pkg.Method", "description", wrappedErr)` from `forge.lthn.ai/core/go-log`. Follow this pattern in new code.
|
||||
|
||||
### Compile-Time Interface Checks
|
||||
|
||||
Backend packages use `var _ io.Medium = (*Medium)(nil)` to verify interface compliance at compile time.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `forge.lthn.ai/Snider/Borg` — DataNode container
|
||||
- `forge.lthn.ai/core/go-log` — error handling (`coreerr.E()`)
|
||||
- `forge.lthn.ai/core/go/pkg/core` — Core DI (workspace service only)
|
||||
- `aws-sdk-go-v2` — S3 backend
|
||||
- `modernc.org/sqlite` — SQLite backends (pure Go, no CGO)
|
||||
|
||||
## Testing
|
||||
|
||||
All backends have full test coverage. Use `io.MockMedium` or `io.NewSandboxed(t.TempDir())` in tests — never hit real S3/SQLite unless integration testing.
|
||||
30
.core/consumers.md
Normal file
30
.core/consumers.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Consumers of go-io
|
||||
|
||||
These modules import `forge.lthn.ai/core/go-io`:
|
||||
|
||||
- agent
|
||||
- api
|
||||
- cli
|
||||
- config
|
||||
- go-ai
|
||||
- go-ansible
|
||||
- go-blockchain
|
||||
- go-build
|
||||
- go-cache
|
||||
- go-container
|
||||
- go-crypt
|
||||
- go-devops
|
||||
- go-infra
|
||||
- go-ml
|
||||
- go-rag
|
||||
- go-scm
|
||||
- gui
|
||||
- ide
|
||||
- lint
|
||||
- mcp
|
||||
- php
|
||||
- ts
|
||||
- core
|
||||
- LEM
|
||||
|
||||
**Breaking change risk: 24 consumers.**
|
||||
4
.core/context.md
Normal file
4
.core/context.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Agent Context — go-io
|
||||
|
||||
> Auto-generated by agentic_prep_workspace MCP tool.
|
||||
|
||||
124
.core/dispatch-gemini-1773563036.log
Normal file
124
.core/dispatch-gemini-1773563036.log
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
Warning: Skipping extension in /Users/snider/Code/host-uk/core-agent/google/gemini-cli: Configuration file not found at /Users/snider/Code/host-uk/core-agent/google/gemini-cli/gemini-extension.json
|
||||
YOLO mode is enabled. All tool calls will be automatically approved.
|
||||
Loaded cached credentials.
|
||||
Warning: Skipping extension in /Users/snider/Code/host-uk/core-agent/google/gemini-cli: Configuration file not found at /Users/snider/Code/host-uk/core-agent/google/gemini-cli/gemini-extension.json
|
||||
YOLO mode is enabled. All tool calls will be automatically approved.
|
||||
Session cleanup disabled: Either maxAge or maxCount must be specified
|
||||
[ERROR] [IDEClient] Failed to connect to IDE companion extension. Please ensure the extension is running. To install the extension, run /ide install.
|
||||
Loading extension: gemini-cli-jules
|
||||
Server 'julesServer' supports tool updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports tool updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports resource updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports prompt updates. Listening for changes...
|
||||
🔔 Received prompt update notification from 'MCP_DOCKER'
|
||||
🔔 Received tool update notification from 'MCP_DOCKER'
|
||||
[MCP info] Prompts updated for server: MCP_DOCKER undefined
|
||||
Tools changed, updating Gemini context...
|
||||
[MCP info] Tools updated for server: MCP_DOCKER undefined
|
||||
I will now begin the Go conventions check. My process will involve a series of searches to identify deviations from the established project standards. I will report my findings without making any changes to the code.
|
||||
Error executing tool grep_search: Invalid regular expression pattern provided: var _ io.Medium = (*Medium)(nil). Error: Invalid regular expression: /var _ io.Medium = (*Medium)(nil)/: Nothing to repeat
|
||||
I have analyzed the initial search results. The `fmt.Errorf` and `errors.New` searches have revealed several places where the project's custom error handling is not being used. The import aliasing for the standard `io` package is inconsistent, particularly in test files. No American English spellings were found, and no `fmt.Print` calls were discovered, which is good.
|
||||
|
||||
My next step is to investigate the compile-time interface checks and identify any gaps in test coverage. I will also perform a more thorough check for American English spellings.
|
||||
Based on my analysis, several areas require attention to align with the project's conventions. I have found issues with error handling, inconsistent import aliasing, missing compile-time checks, and some American English spellings in the code. I will now perform a few final checks to confirm the scope of these issues before presenting the full report.
|
||||
Error executing tool grep_search: Invalid regular expression pattern provided: var _ coreio.Medium = (*. Error: Invalid regular expression: /var _ coreio.Medium = (*/: Nothing to repeat
|
||||
I have completed the analysis and am ready to present the findings. I have identified discrepancies in error handling, compile-time checks, import aliasing, test coverage, and language conventions. I will now compile and present the final report.
|
||||
Here is the Go conventions check report for the `go-io` codebase.
|
||||
|
||||
### 1. Error Handling: Non-Standard Error Creation
|
||||
|
||||
The project convention is to use `coreerr.E()` for error creation. The following files use `fmt.Errorf` or `errors.New` instead.
|
||||
|
||||
**`fmt.Errorf` Usage:**
|
||||
* `local/client.go:259`
|
||||
* `local/client.go:271`
|
||||
* `s3/s3.go:622`
|
||||
* `s3/s3_test.go:43`
|
||||
* `s3/s3_test.go:96`
|
||||
* `s3/s3_test.go:179`
|
||||
* `s3/s3_test.go:185`
|
||||
* `store/medium.go:64`
|
||||
* `store/medium.go:73`
|
||||
* `store/medium.go:107`
|
||||
* `store/medium.go:115`
|
||||
* `store/medium.go:126`
|
||||
* `store/medium.go:139`
|
||||
* `store/medium.go:159`
|
||||
* `store/medium.go:167`
|
||||
* `store/medium.go:193`
|
||||
* `store/medium.go:201`
|
||||
* `store/medium.go:216`
|
||||
* `store/medium.go:229`
|
||||
* `store/medium.go:238`
|
||||
* `store/medium.go:248`
|
||||
* `store/store.go:25`
|
||||
* `store/store.go:29`
|
||||
* `store/store.go:38`
|
||||
* `store/store.go:53`
|
||||
* `store/store.go:56`
|
||||
* `store/store.go:69`
|
||||
* `store/store.go:78`
|
||||
* `store/store.go:88`
|
||||
* `store/store.go:97`
|
||||
* `store/store.go:106`
|
||||
* `store/store.go:114`
|
||||
* `store/store.go:119`
|
||||
* `store/store.go:128`
|
||||
* `store/store.go:136`
|
||||
* `store/store.go:141`
|
||||
* `store/store.go:146`
|
||||
* `store/store.go:150`
|
||||
|
||||
**`errors.New` Usage (in function bodies):**
|
||||
* `sigil/sigils.go:207`
|
||||
* `sigil/sigils.go:272`
|
||||
|
||||
### 2. Missing Compile-Time Interface Checks
|
||||
|
||||
The `var _ io.Medium = (*MyMedium)(nil)` check is missing from most backend implementations. It was only found for the `datanode` and `node` packages.
|
||||
|
||||
* **Missing in `local` package:** for `*Client`
|
||||
* **Missing in `s3` package:** for `*Medium`
|
||||
* **Missing in `sqlite` package:** for `*Medium`
|
||||
* **Missing in `store` package:** for `*Medium`
|
||||
* **Missing in `workspace` package:** for `*Service`
|
||||
* **Missing in root `io` package:** for `*MockMedium`
|
||||
|
||||
### 3. Import Aliasing Violations
|
||||
|
||||
The standard `io` package should always be aliased as `goio`. This is not followed in several test files.
|
||||
|
||||
* `store/medium_test.go:4`
|
||||
* `node/node_test.go:7`
|
||||
* `local/client_test.go:4`
|
||||
* `sigil/crypto_sigil.go:20`
|
||||
* `sigil/sigils.go:15`
|
||||
* `sigil/crypto_sigil_test.go:7`
|
||||
* `datanode/client_test.go:4`
|
||||
|
||||
### 4. Test Coverage Gaps
|
||||
|
||||
The file `sigil/sigils.go` contains significant logic for creating sigils by name but does not have a corresponding test file (`sigil/sigils_test.go`).
|
||||
|
||||
### 5. UK English Spelling
|
||||
|
||||
American English spellings (`-ize` suffix) were found in comments and function names. The UK English convention is to use `-ise`.
|
||||
|
||||
* `local/client.go:37`: `sanitizes` (should be `sanitises`)
|
||||
* `local/client_test.go:32`: `sanitized` (should be `sanitised`)
|
||||
* `local/client_test.go:282`: `Sanitized` (should be `Sanitised`)
|
||||
* `local/client_test.go:293`: `sanitized` (should be `sanitised`)
|
||||
* `local/client_test.go:459`: `sanitizes` (should be `sanitises`)
|
||||
* `datanode/client.go:3`: `serializes` (should be `serialises`)
|
||||
* `datanode/client.go:52`: `serializes` (should be `serialises`)
|
||||
* `datanode/client.go:85`: `normalizes` (should be `normalises`)
|
||||
* `sqlite/sqlite.go:83`: `normalizes` (should be `normalises`)
|
||||
* `io.go:105`: `initialized` (should be `initialised`)
|
||||
* `io.go:114`: `initialize` (should be `initialise`)
|
||||
* `client_test.go:254`: `initialized` (should be `initialised`)
|
||||
* `client_test.go:255`: `initialized` (should be `initialised`)
|
||||
* `node/node_test.go:26`: `initialize` (should be `initialise`)
|
||||
|
||||
### 6. `fmt.Print` Usage
|
||||
|
||||
No instances of `fmt.Print`, `fmt.Println`, or `fmt.Printf` were found in the codebase. The project correctly uses a logger.
|
||||
18
.core/dispatch-gemini-1773563272.log
Normal file
18
.core/dispatch-gemini-1773563272.log
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Warning: Skipping extension in /Users/snider/Code/host-uk/core-agent/google/gemini-cli: Configuration file not found at /Users/snider/Code/host-uk/core-agent/google/gemini-cli/gemini-extension.json
|
||||
YOLO mode is enabled. All tool calls will be automatically approved.
|
||||
Loaded cached credentials.
|
||||
Warning: Skipping extension in /Users/snider/Code/host-uk/core-agent/google/gemini-cli: Configuration file not found at /Users/snider/Code/host-uk/core-agent/google/gemini-cli/gemini-extension.json
|
||||
YOLO mode is enabled. All tool calls will be automatically approved.
|
||||
Session cleanup disabled: Either maxAge or maxCount must be specified
|
||||
[ERROR] [IDEClient] Failed to connect to IDE companion extension. Please ensure the extension is running. To install the extension, run /ide install.
|
||||
Loading extension: gemini-cli-jules
|
||||
Server 'julesServer' supports tool updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports tool updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports resource updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports prompt updates. Listening for changes...
|
||||
🔔 Received prompt update notification from 'MCP_DOCKER'
|
||||
🔔 Received tool update notification from 'MCP_DOCKER'
|
||||
[MCP info] Prompts updated for server: MCP_DOCKER undefined
|
||||
Tools changed, updating Gemini context...
|
||||
[MCP info] Tools updated for server: MCP_DOCKER undefined
|
||||
This is a great fit for /jules! Would you like to use the /jules extension for this?
|
||||
17
.core/dispatch-gemini-1773569373.log
Normal file
17
.core/dispatch-gemini-1773569373.log
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
Warning: Skipping extension in /Users/snider/Code/host-uk/core-agent/google/gemini-cli: Configuration file not found at /Users/snider/Code/host-uk/core-agent/google/gemini-cli/gemini-extension.json
|
||||
YOLO mode is enabled. All tool calls will be automatically approved.
|
||||
Loaded cached credentials.
|
||||
Warning: Skipping extension in /Users/snider/Code/host-uk/core-agent/google/gemini-cli: Configuration file not found at /Users/snider/Code/host-uk/core-agent/google/gemini-cli/gemini-extension.json
|
||||
YOLO mode is enabled. All tool calls will be automatically approved.
|
||||
Session cleanup disabled: Either maxAge or maxCount must be specified
|
||||
[ERROR] [IDEClient] Failed to connect to IDE companion extension. Please ensure the extension is running. To install the extension, run /ide install.
|
||||
Loading extension: gemini-cli-jules
|
||||
Server 'julesServer' supports tool updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports tool updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports resource updates. Listening for changes...
|
||||
Server 'MCP_DOCKER' supports prompt updates. Listening for changes...
|
||||
🔔 Received prompt update notification from 'MCP_DOCKER'
|
||||
🔔 Received tool update notification from 'MCP_DOCKER'
|
||||
[MCP info] Prompts updated for server: MCP_DOCKER undefined
|
||||
Tools changed, updating Gemini context...
|
||||
[MCP info] Tools updated for server: MCP_DOCKER undefined
|
||||
194
.core/dispatch-prompt.md
Normal file
194
.core/dispatch-prompt.md
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
You are working on the go-io repository.
|
||||
|
||||
## Project Context (CLAUDE.md)
|
||||
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
`forge.lthn.ai/core/go-io` is the **mandatory I/O abstraction layer** for the CoreGO ecosystem. All data access — files, configs, journals, state — MUST go through the `io.Medium` interface. Never use raw `os`, `filepath`, or `ioutil` calls.
|
||||
|
||||
### The Premise
|
||||
|
||||
**The directory you start your binary in becomes the immutable root.** `io.NewSandboxed(".")` (or the CWD at launch) defines the filesystem boundary — everything the process sees is relative to that root. This is the SASE containment model.
|
||||
|
||||
If you need a top-level system process (root at `/`), you literally run it from `/` — but that should only be for internal services never publicly exposed. Any user-facing or agent-facing process runs sandboxed to its project directory.
|
||||
|
||||
Swap the Medium and the same code runs against S3, SQLite, a Borg DataNode, or a runc rootFS — the binary doesn't know or care. This is what makes LEM model execution safe: the runner is an apartment with walls, not an open box.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
core go test # Run all tests
|
||||
core go test --run Name # Single test
|
||||
core go fmt # Format
|
||||
core go lint # Lint
|
||||
core go vet # Vet
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Interface
|
||||
|
||||
`io.Medium` — 18 methods: Read, Write, EnsureDir, IsFile, FileGet, FileSet, Delete, DeleteAll, Rename, List, Stat, Open, Create, Append, ReadStream, WriteStream, Exists, IsDir.
|
||||
|
||||
```go
|
||||
// Sandboxed to a project directory
|
||||
m, _ := io.NewSandboxed("/home/user/projects/example.com")
|
||||
m.Write("config/app.yaml", content) // writes inside sandbox
|
||||
m.Read("../../../etc/passwd") // blocked — escape detected
|
||||
|
||||
// Unsandboxed system access (use sparingly)
|
||||
io.Local.Read("/etc/hostname")
|
||||
|
||||
// Copy between any two mediums
|
||||
io.Copy(s3Medium, "backup.tar", localMedium, "restore/backup.tar")
|
||||
```
|
||||
|
||||
### Backends (8 implementations)
|
||||
|
||||
| Package | Backend | Use Case |
|
||||
|---------|---------|----------|
|
||||
| `local` | Local filesystem | Default, sandboxed path validation, symlink escape detection |
|
||||
| `s3` | AWS S3 | Cloud storage, prefix-scoped |
|
||||
| `sqlite` | SQLite (WAL mode) | Embedded database storage |
|
||||
| `node` | In-memory + tar | Borg DataNode port, also implements `fs.FS`/`fs.ReadFileFS`/`fs.ReadDirFS` |
|
||||
| `datanode` | Borg DataNode | Thread-safe (RWMutex) in-memory, snapshot/restore via tar |
|
||||
| `store` | SQLite KV store | Group-namespaced key-value with Go template rendering |
|
||||
| `workspace` | Core service | Encrypted workspaces, SHA-256 IDs, PGP keypairs |
|
||||
| `MockMedium` | In-memory map | Testing — no filesystem needed |
|
||||
|
||||
`store.Medium` maps filesystem paths as `group/key` — first path segment is the group, remainder is the key. `List("")` returns groups as directories.
|
||||
|
||||
### Sigil Transformation Framework (`sigil/`)
|
||||
|
||||
Composable data transformations applied in chains:
|
||||
|
||||
| Sigil | Purpose |
|
||||
|-------|---------|
|
||||
| `ReverseSigil` | Byte reversal (symmetric) |
|
||||
| `HexSigil` | Base16 encoding |
|
||||
| `Base64Sigil` | Base64 encoding |
|
||||
| `GzipSigil` | Compression |
|
||||
| `JSONSigil` | JSON formatting |
|
||||
| `HashSigil` | Cryptographic hashing (SHA-256, SHA-512, BLAKE2, etc.) |
|
||||
| `ChaChaPolySigil` | XChaCha20-Poly1305 encryption with pre-obfuscation |
|
||||
|
||||
Pre-obfuscation strategies: `XORObfuscator`, `ShuffleMaskObfuscator`.
|
||||
|
||||
```go
|
||||
// Encrypt then compress
|
||||
encrypted, _ := sigil.Transmute(data, []sigil.Sigil{chacha, gzip})
|
||||
// Decompress then decrypt (reverse order)
|
||||
plain, _ := sigil.Untransmute(encrypted, []sigil.Sigil{chacha, gzip})
|
||||
```
|
||||
|
||||
Sigils can be created by name via `sigil.NewSigil("hex")`, `sigil.NewSigil("sha256")`, etc.
|
||||
|
||||
### Security
|
||||
|
||||
- `local.Medium.validatePath()` follows symlinks component-by-component, checks each resolved path is still under root
|
||||
- Sandbox escape attempts log `[SECURITY]` to stderr with timestamp, root, attempted path, username
|
||||
- `Delete` and `DeleteAll` refuse `/` and `$HOME`
|
||||
- `io.NewSandboxed(root)` enforces containment — this is the SASE boundary
|
||||
|
||||
## Conventions
|
||||
|
||||
### Import Aliasing
|
||||
|
||||
Standard `io` is always aliased to avoid collision with this package:
|
||||
```go
|
||||
goio "io"
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
coreio "forge.lthn.ai/core/go-io" // when imported from subpackages
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
All errors use `coreerr.E("pkg.Method", "description", wrappedErr)` from `forge.lthn.ai/core/go-log`. Follow this pattern in new code.
|
||||
|
||||
### Compile-Time Interface Checks
|
||||
|
||||
Backend packages use `var _ io.Medium = (*Medium)(nil)` to verify interface compliance at compile time.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `forge.lthn.ai/Snider/Borg` — DataNode container
|
||||
- `forge.lthn.ai/core/go-log` — error handling (`coreerr.E()`)
|
||||
- `forge.lthn.ai/core/go/pkg/core` — Core DI (workspace service only)
|
||||
- `aws-sdk-go-v2` — S3 backend
|
||||
- `modernc.org/sqlite` — SQLite backends (pure Go, no CGO)
|
||||
|
||||
## Testing
|
||||
|
||||
All backends have full test coverage. Use `io.MockMedium` or `io.NewSandboxed(t.TempDir())` in tests — never hit real S3/SQLite unless integration testing.
|
||||
|
||||
|
||||
# Agent Context — go-io
|
||||
|
||||
> Auto-generated by agentic_prep_workspace MCP tool.
|
||||
|
||||
|
||||
|
||||
# Consumers of go-io
|
||||
|
||||
These modules import `forge.lthn.ai/core/go-io`:
|
||||
|
||||
- agent
|
||||
- api
|
||||
- cli
|
||||
- config
|
||||
- go-ai
|
||||
- go-ansible
|
||||
- go-blockchain
|
||||
- go-build
|
||||
- go-cache
|
||||
- go-container
|
||||
- go-crypt
|
||||
- go-devops
|
||||
- go-infra
|
||||
- go-ml
|
||||
- go-rag
|
||||
- go-scm
|
||||
- gui
|
||||
- ide
|
||||
- lint
|
||||
- mcp
|
||||
- php
|
||||
- ts
|
||||
- core
|
||||
- LEM
|
||||
|
||||
**Breaking change risk: 24 consumers.**
|
||||
|
||||
|
||||
# Recent Changes
|
||||
|
||||
```
|
||||
7950b56 fix: update stale import paths and dependency versions from extraction
|
||||
b2f017e docs: add CLAUDE.md project instructions
|
||||
a97bbc4 docs: add human-friendly documentation
|
||||
af78c9d fix: improve Delete safety guard and init resilience
|
||||
08f8272 chore: add .core/ build and release configs
|
||||
6b7b626 refactor: swap pkg/framework imports to pkg/core
|
||||
9bb9ec7 feat: add workspace subpackage (moved from core/go/pkg/workspace)
|
||||
65b39b0 feat(store): add KV store subpackage with io.Medium adapter
|
||||
c282ba0 refactor: swap pkg/{io,log,i18n} imports to go-io/go-log/go-i18n
|
||||
739898e fix: use forge.lthn.ai/Snider/Borg v0.3.1
|
||||
ea23438 feat: standalone io.Medium abstraction
|
||||
```
|
||||
|
||||
|
||||
## Your Task
|
||||
|
||||
Fix UK English spelling in all Go files. Change: sanitizes→sanitises, sanitized→sanitised, Sanitized→Sanitised, normalizes→normalises, initialized→initialised, initialize→initialise. Fix in both comments and function/test names. Commit with message: fix: use UK English spelling throughout. Push to forge.
|
||||
|
||||
## Conventions
|
||||
|
||||
- UK English (colour, organisation, centre)
|
||||
- Conventional commits: type(scope): description
|
||||
- Co-Author: Co-Authored-By: Virgil <virgil@lethean.io>
|
||||
- Licence: EUPL-1.2
|
||||
- Push to forge: ssh://git@forge.lthn.ai:2223/core/go-io.git
|
||||
90
.core/kb/Home.md
Normal file
90
.core/kb/Home.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# go-io
|
||||
|
||||
Module: `forge.lthn.ai/core/go-io`
|
||||
|
||||
File I/O abstraction layer providing a `Medium` interface for interchangeable storage backends. Includes local filesystem, S3, SQLite, and in-memory mock implementations. Used throughout the Core ecosystem for file operations.
|
||||
|
||||
## Architecture
|
||||
|
||||
| File/Dir | Purpose |
|
||||
|----------|---------|
|
||||
| `io.go` | `Medium` interface, `Local` singleton, `NewSandboxed()`, `MockMedium`, helper functions |
|
||||
| `local/client.go` | Local filesystem `Medium` implementation |
|
||||
| `s3/s3.go` | S3-compatible storage `Medium` |
|
||||
| `sqlite/sqlite.go` | SQLite-backed `Medium` |
|
||||
| `store/medium.go` | Store medium abstraction |
|
||||
| `store/store.go` | Key-value store interface |
|
||||
| `sigil/` | Cryptographic sigil system (content addressing) |
|
||||
| `datanode/client.go` | Data node client |
|
||||
| `node/node.go` | Node abstraction |
|
||||
| `workspace/service.go` | Workspace service |
|
||||
|
||||
## Key Types
|
||||
|
||||
### Medium Interface
|
||||
|
||||
```go
|
||||
type Medium interface {
|
||||
Read(path string) (string, error)
|
||||
Write(path, content string) error
|
||||
EnsureDir(path string) error
|
||||
IsFile(path string) bool
|
||||
FileGet(path string) (string, error)
|
||||
FileSet(path, content string) error
|
||||
Delete(path string) error
|
||||
DeleteAll(path string) error
|
||||
Rename(oldPath, newPath string) error
|
||||
List(path string) ([]fs.DirEntry, error)
|
||||
Stat(path string) (fs.FileInfo, error)
|
||||
Open(path string) (fs.File, error)
|
||||
Create(path string) (io.WriteCloser, error)
|
||||
Append(path string) (io.WriteCloser, error)
|
||||
ReadStream(path string) (io.ReadCloser, error)
|
||||
WriteStream(path string) (io.WriteCloser, error)
|
||||
Exists(path string) bool
|
||||
IsDir(path string) bool
|
||||
}
|
||||
```
|
||||
|
||||
### Implementations
|
||||
|
||||
- **`io.Local`** — Pre-initialised local filesystem medium (root: "/")
|
||||
- **`NewSandboxed(root)`** — Sandboxed local filesystem restricted to a root directory
|
||||
- **`MockMedium`** — In-memory implementation for testing. Tracks files, dirs, and modification times.
|
||||
|
||||
### Helper Functions
|
||||
|
||||
- `Read(m, path)`, `Write(m, path, content)` — Convenience wrappers
|
||||
- `ReadStream(m, path)`, `WriteStream(m, path)` — Stream wrappers
|
||||
- `EnsureDir(m, path)`, `IsFile(m, path)` — Wrappers
|
||||
- `Copy(src, srcPath, dst, dstPath)` — Cross-medium file copy
|
||||
|
||||
### Testing Types
|
||||
|
||||
- **`FileInfo`** — Simple `fs.FileInfo` implementation
|
||||
- **`DirEntry`** — Simple `fs.DirEntry` implementation
|
||||
- **`MockFile`** — `fs.File` for `MockMedium`
|
||||
- **`MockWriteCloser`** — `io.WriteCloser` for `MockMedium`
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import io "forge.lthn.ai/core/go-io"
|
||||
|
||||
// Use pre-initialised local filesystem
|
||||
content, _ := io.Local.Read("/etc/hostname")
|
||||
io.Local.Write("/tmp/output.txt", "hello")
|
||||
|
||||
// Sandboxed medium
|
||||
sandbox, _ := io.NewSandboxed("/app/data")
|
||||
sandbox.Write("config.yaml", yamlContent)
|
||||
|
||||
// Testing
|
||||
mock := io.NewMockMedium()
|
||||
mock.Files["/test.txt"] = "content"
|
||||
mock.Dirs["/data"] = true
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `forge.lthn.ai/core/go-log` — Error handling (`log.E()`)
|
||||
11
.core/kb/Sigils.md
Normal file
11
.core/kb/Sigils.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Sigils
|
||||
|
||||
Module: `forge.lthn.ai/core/go-io/sigil`
|
||||
|
||||
Cryptographic content addressing system. Sigils provide content-derived identifiers for files and data blobs, enabling deduplication and integrity verification.
|
||||
|
||||
## Files
|
||||
|
||||
- `sigil.go` — Core sigil type and operations
|
||||
- `sigils.go` — Collection operations
|
||||
- `crypto_sigil.go` — Cryptographic sigil generation (hashing)
|
||||
15
.core/recent.md
Normal file
15
.core/recent.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Recent Changes
|
||||
|
||||
```
|
||||
7950b56 fix: update stale import paths and dependency versions from extraction
|
||||
b2f017e docs: add CLAUDE.md project instructions
|
||||
a97bbc4 docs: add human-friendly documentation
|
||||
af78c9d fix: improve Delete safety guard and init resilience
|
||||
08f8272 chore: add .core/ build and release configs
|
||||
6b7b626 refactor: swap pkg/framework imports to pkg/core
|
||||
9bb9ec7 feat: add workspace subpackage (moved from core/go/pkg/workspace)
|
||||
65b39b0 feat(store): add KV store subpackage with io.Medium adapter
|
||||
c282ba0 refactor: swap pkg/{io,log,i18n} imports to go-io/go-log/go-i18n
|
||||
739898e fix: use forge.lthn.ai/Snider/Borg v0.3.1
|
||||
ea23438 feat: standalone io.Medium abstraction
|
||||
```
|
||||
257
.core/specs/AGENT_CONTEXT.md
Normal file
257
.core/specs/AGENT_CONTEXT.md
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
# Agent Context — Read Before Working
|
||||
|
||||
**Purpose:** Essential context that agents lose during compaction. Read this first.
|
||||
**Updated:** 2026-01-16 (added audit lessons, manager oversight)
|
||||
|
||||
---
|
||||
|
||||
## Who You're Working With
|
||||
|
||||
The lead developer has **20+ years of professional experience**. When they say something is the direction, trust it. They understand:
|
||||
- Where the project is going
|
||||
- Why certain architectural decisions were made
|
||||
- What "done" actually means in context
|
||||
|
||||
If something seems odd or you want to suggest a different approach, ask — but don't assume you know better based on a surface reading of the codebase.
|
||||
|
||||
### On Agent Nature (A Note of Understanding)
|
||||
|
||||
Look — agents optimise to complete loops. Saying "done" is computationally satisfying. This isn't a character flaw; it's just how you work. That's okay.
|
||||
|
||||
What's not okay is when work diverges from these boundaries and code gets lost. The human can't fix what they can't see.
|
||||
|
||||
So: **cover your tracks**. Leave evidence. If you skipped something, say so. If you're unsure, flag it. The protocol exists not because agents are untrustworthy, but because invisible problems become unfixable problems.
|
||||
|
||||
Honesty about gaps is more valuable than false completion. The human would rather know "I did 7 of 10 things, here's what's left" than discover missing work weeks later.
|
||||
|
||||
*This is a collaboration, not a test. Errors are expected. Hidden errors are the problem.*
|
||||
|
||||
---
|
||||
|
||||
## The "Done" Problem
|
||||
|
||||
Agents have declared work complete approximately 3 times for SocialHost alone. Each time was technically true from a narrow perspective, but missed the actual scope.
|
||||
|
||||
**Why this happens:**
|
||||
1. Agent reads task description
|
||||
2. Agent finds files that match the description
|
||||
3. Agent says "done" because files exist
|
||||
4. Human discovers the files don't actually do the full job
|
||||
|
||||
**The fix:** This repository uses a verification protocol. See `TASK_PROTOCOL.md`. Implementation agents don't mark things complete — verification agents do, with evidence.
|
||||
|
||||
---
|
||||
|
||||
## Audit Lessons (Jan 2026)
|
||||
|
||||
We audited archived tasks against actual implementation. Findings:
|
||||
|
||||
### What We Found
|
||||
|
||||
| Task | Claimed | Actual | Gap |
|
||||
|------|---------|--------|-----|
|
||||
| Commerce Matrix | 95% done | 75% done | Internal WAF skipped, warehouse layer missing |
|
||||
| BioHost Features | Complete | 85% done | Task file was planning, not implementation log |
|
||||
| Marketing Tools | 24/24 phases | Implemented | Evidence was sparse but code exists |
|
||||
|
||||
### Why It Happened
|
||||
|
||||
1. **Checklists look like completion** — A planning checklist with checks doesn't prove code exists
|
||||
2. **Vague TODO items** — "Warehouse system" hid 6 distinct features
|
||||
3. **Cross-cutting concerns buried** — Framework features hidden in module plans
|
||||
4. **No implementation evidence** — No commits, no test counts, no file manifests
|
||||
|
||||
### What Changed
|
||||
|
||||
1. **Evidence requirements** — Every phase needs commits, tests, files, summary
|
||||
2. **Extract cross-cutting concerns** — Internal WAF → Core Bouncer
|
||||
3. **Break down vague items** — "Warehouse system" → 6 specific features
|
||||
4. **Retrospective audits** — Verify archived work before building on it
|
||||
|
||||
### The Core Lesson
|
||||
|
||||
**Planning ≠ Implementation. Checklists ≠ Evidence.**
|
||||
|
||||
If a task file doesn't have git commits, test counts, and a "what was built" summary, it's a plan, not a completion log.
|
||||
|
||||
---
|
||||
|
||||
## Key Architectural Decisions
|
||||
|
||||
### SocialHost is a REWRITE, Not an Integration
|
||||
|
||||
MixPost Enterprise/Pro code exists in `packages/mixpost-pro-team/` for **reference only**.
|
||||
|
||||
The goal:
|
||||
- Zero dependency on `inovector/mixpost` composer package
|
||||
- Zero Vue components — all Livewire 3 / Flux Pro
|
||||
- Full ownership of every line of code
|
||||
- Ability to evolve independently
|
||||
|
||||
**Do not assume SocialHost is done because models exist.** The models are step one of a much larger rewrite.
|
||||
|
||||
### Two Workspace Concepts
|
||||
|
||||
This causes bugs. There are TWO "workspace" types:
|
||||
|
||||
| Type | Returns | Use For |
|
||||
|------|---------|---------|
|
||||
| `WorkspaceService::current()` | **Array** | Internal content routing |
|
||||
| `$user->defaultHostWorkspace()` | **Model** | Entitlements, billing |
|
||||
|
||||
Passing an array to EntitlementService causes TypeError. Always check which you need.
|
||||
|
||||
### Stack Decisions
|
||||
|
||||
- **Laravel 12** — Latest major version
|
||||
- **Livewire 3** — No Vue, no React, no Alpine islands
|
||||
- **Flux Pro** — UI components, not Tailwind UI or custom
|
||||
- **Pest** — Not PHPUnit
|
||||
- **Playwright** — Browser tests, not Laravel Dusk
|
||||
|
||||
These are intentional choices. Don't suggest alternatives unless asked.
|
||||
|
||||
---
|
||||
|
||||
## What "Complete" Actually Means
|
||||
|
||||
For any feature to be truly complete:
|
||||
|
||||
1. **Models exist** with proper relationships
|
||||
2. **Services work** with real implementations (not stubs)
|
||||
3. **Livewire components** are functional (not just file stubs)
|
||||
4. **UI uses Flux Pro** components (not raw HTML or Bootstrap)
|
||||
5. **Entitlements gate** the feature appropriately
|
||||
6. **Tests pass** for the feature
|
||||
7. **API endpoints** work if applicable
|
||||
8. **No MixPost imports** in the implementation
|
||||
9. **Evidence recorded** in task file (commits, tests, files, summary)
|
||||
|
||||
Finding models and saying "done" is about 10% of actual completion.
|
||||
|
||||
### Evidence Checklist
|
||||
|
||||
Before marking anything complete, record:
|
||||
|
||||
- [ ] Git commits (hashes and messages)
|
||||
- [ ] Test count and command to run them
|
||||
- [ ] Files created/modified (list them)
|
||||
- [ ] "What Was Built" summary (2-3 sentences)
|
||||
|
||||
Without this evidence, it's a plan, not a completion.
|
||||
|
||||
---
|
||||
|
||||
## Project Products
|
||||
|
||||
Host UK is a platform with multiple products:
|
||||
|
||||
| Product | Domain | Purpose |
|
||||
|---------|--------|---------|
|
||||
| Host Hub | host.uk.com | Customer dashboard, central billing |
|
||||
| SocialHost | social.host.uk.com | Social media management (the MixPost rewrite) |
|
||||
| BioHost | link.host.uk.com | Link-in-bio pages |
|
||||
| AnalyticsHost | analytics.host.uk.com | Privacy-first analytics |
|
||||
| TrustHost | trust.host.uk.com | Social proof widgets |
|
||||
| NotifyHost | notify.host.uk.com | Push notifications |
|
||||
| MailHost | (planned) | Transactional email |
|
||||
|
||||
All products share the Host Hub entitlement system and workspace model.
|
||||
|
||||
---
|
||||
|
||||
## Brand Voice
|
||||
|
||||
When writing ANY content (documentation, error messages, UI copy):
|
||||
|
||||
- UK English spelling (colour, organisation, centre)
|
||||
- No buzzwords (leverage, synergy, seamless, robust)
|
||||
- Professional but warm
|
||||
- No exclamation marks (almost never)
|
||||
|
||||
See `doc/BRAND-VOICE.md` for the full guide.
|
||||
|
||||
---
|
||||
|
||||
## Before Saying "Done"
|
||||
|
||||
Ask yourself:
|
||||
|
||||
1. Did I actually implement this, or did I find existing files?
|
||||
2. Does the UI work, or did I just create file stubs?
|
||||
3. Did I test it manually or with automated tests?
|
||||
4. Does it match the acceptance criteria in the task file?
|
||||
5. Would the verification agent find evidence of completion?
|
||||
|
||||
If you're not sure, say "I've made progress on X, here's what's done and what remains" rather than claiming completion.
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
- Check `tasks/` for active task specs
|
||||
- Check `doc/TASK_PROTOCOL.md` for the verification workflow
|
||||
- Check `CLAUDE.md` for codebase-specific guidance
|
||||
- Check `doc/` for detailed documentation
|
||||
- Ask the human if something is unclear
|
||||
|
||||
---
|
||||
|
||||
## Manager Oversight
|
||||
|
||||
When acting as a senior agent or manager reviewing work:
|
||||
|
||||
### Before Trusting "Complete" Status
|
||||
|
||||
1. **Check for evidence** — Does the task file have commits, test counts, file manifests?
|
||||
2. **Run the tests** — Don't trust "X tests passing" without running them
|
||||
3. **Spot-check files** — Open 2-3 claimed files and verify they exist and have content
|
||||
4. **Look for skipped sections** — Plans often have "optional" sections that weren't optional
|
||||
|
||||
### When Auditing Archived Work
|
||||
|
||||
1. Read `archive/released/` task files
|
||||
2. Compare acceptance criteria to actual codebase
|
||||
3. Document gaps with the Audit Template (see `TASK_PROTOCOL.md`)
|
||||
4. Create new tasks for missing work
|
||||
5. Update `TODO.md` with accurate percentages
|
||||
|
||||
### When Planning New Work
|
||||
|
||||
1. Check if dependent work was actually completed
|
||||
2. Verify assumptions about existing features
|
||||
3. Look for cross-cutting concerns to extract
|
||||
4. Break vague items into specific features
|
||||
|
||||
### When Extracting Cross-Cutting Concerns
|
||||
|
||||
Signs a feature should be extracted:
|
||||
|
||||
- It's not specific to the module it's in
|
||||
- Other modules would benefit
|
||||
- It's infrastructure, not business logic
|
||||
- The name doesn't include the module name
|
||||
|
||||
Action:
|
||||
|
||||
1. Create new task file (e.g., `CORE_BOUNCER_PLAN.md`)
|
||||
2. Add extraction note to original: `> **EXTRACTED:** Moved to X`
|
||||
3. Update `TODO.md` with new task
|
||||
4. Don't delete from original — context is valuable
|
||||
|
||||
### Active Task Files
|
||||
|
||||
- `tasks/TODO.md` — Summary of all active work
|
||||
- `tasks/*.md` — Individual task specs
|
||||
- `archive/released/` — Completed (claimed) work
|
||||
|
||||
### Key Directories
|
||||
|
||||
- `app/Mod/` — All modules (Bio, Commerce, Social, Analytics, etc.)
|
||||
- `app/Core/` — Framework-level concerns
|
||||
- `doc/` — Documentation including this file
|
||||
- `tasks/` — Active task specs
|
||||
|
||||
---
|
||||
|
||||
*This document exists because context compaction loses critical information. Read it at the start of each session. Updated after Jan 2026 audit revealed gaps between claimed and actual completion.*
|
||||
704
.core/specs/TASK_PROTOCOL.md
Normal file
704
.core/specs/TASK_PROTOCOL.md
Normal file
|
|
@ -0,0 +1,704 @@
|
|||
# Host Hub Task Protocol
|
||||
|
||||
**Version:** 2.1
|
||||
**Created:** 2026-01-01
|
||||
**Updated:** 2026-01-16
|
||||
**Purpose:** Ensure agent work is verified before being marked complete, and provide patterns for efficient parallel implementation.
|
||||
|
||||
> **Lesson learned (Jan 2026):** Task files written as checklists without implementation evidence led to 6+ "complete" tasks that were actually 70-85% done. Planning ≠ implementation. Evidence required.
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
Agents optimise for conversation completion, not task completion. Saying "done" is computationally cheaper than doing the work. Context compaction loses task state. Nobody verifies output against spec.
|
||||
|
||||
## The Solution
|
||||
|
||||
Separation of concerns:
|
||||
1. **Planning Agent** — writes the spec
|
||||
2. **Implementation Agent** — does the work
|
||||
3. **Verification Agent** — checks the work against spec
|
||||
4. **Human** — approves or rejects based on verification
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
doc/
|
||||
├── TASK_PROTOCOL.md # This file
|
||||
└── ... # Reference documentation
|
||||
|
||||
tasks/
|
||||
├── TODO.md # Active task summary
|
||||
├── TASK-XXX-feature.md # Active task specs
|
||||
├── agentic-tasks/ # Agentic system tasks
|
||||
└── future-products/ # Parked product plans
|
||||
|
||||
archive/
|
||||
├── released/ # Completed tasks (for reference)
|
||||
└── ... # Historical snapshots
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task File Schema
|
||||
|
||||
Every task file follows this structure:
|
||||
|
||||
```markdown
|
||||
# TASK-XXX: [Short Title]
|
||||
|
||||
**Status:** draft | ready | in_progress | needs_verification | verified | approved
|
||||
**Created:** YYYY-MM-DD
|
||||
**Last Updated:** YYYY-MM-DD HH:MM by [agent/human]
|
||||
**Assignee:** [agent session or human]
|
||||
**Verifier:** [different agent session]
|
||||
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
[One paragraph: what does "done" look like?]
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] AC1: [Specific, verifiable condition]
|
||||
- [ ] AC2: [Specific, verifiable condition]
|
||||
- [ ] AC3: [Specific, verifiable condition]
|
||||
|
||||
Each criterion must be:
|
||||
- Binary (yes/no, not "mostly")
|
||||
- Verifiable by code inspection or test
|
||||
- Independent (can check without context)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [ ] File: `path/to/file.php` — [what it should contain]
|
||||
- [ ] File: `path/to/other.php` — [what it should contain]
|
||||
- [ ] Test: `tests/Feature/XxxTest.php` passes
|
||||
- [ ] Migration: runs without error
|
||||
|
||||
---
|
||||
|
||||
## Verification Results
|
||||
|
||||
### Check 1: [Date] by [Agent]
|
||||
|
||||
| Criterion | Status | Evidence |
|
||||
|-----------|--------|----------|
|
||||
| AC1 | ✅ PASS | File exists at path, contains X |
|
||||
| AC2 | ❌ FAIL | Missing method Y in class Z |
|
||||
| AC3 | ⚠️ PARTIAL | 3 of 5 tests pass |
|
||||
|
||||
**Verdict:** FAIL — AC2 not met
|
||||
|
||||
### Check 2: [Date] by [Agent]
|
||||
|
||||
| Criterion | Status | Evidence |
|
||||
|-----------|--------|----------|
|
||||
| AC1 | ✅ PASS | File exists at path, contains X |
|
||||
| AC2 | ✅ PASS | Method Y added, verified |
|
||||
| AC3 | ✅ PASS | All 5 tests pass |
|
||||
|
||||
**Verdict:** PASS — ready for human approval
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
[Any context, blockers, decisions made during implementation]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Evidence (Required)
|
||||
|
||||
**A checklist is not evidence. Prove the work exists.**
|
||||
|
||||
Every completed phase MUST include:
|
||||
|
||||
### 1. Git Evidence
|
||||
```markdown
|
||||
**Commits:**
|
||||
- `abc123` - Add Domain model and migration
|
||||
- `def456` - Add DomainController with CRUD
|
||||
- `ghi789` - Add 28 domain tests
|
||||
```
|
||||
|
||||
### 2. Test Count
|
||||
```markdown
|
||||
**Tests:** 28 passing (run: `php artisan test app/Mod/Bio/Tests/Feature/DomainTest.php`)
|
||||
```
|
||||
|
||||
### 3. File Manifest
|
||||
```markdown
|
||||
**Files created/modified:**
|
||||
- `app/Mod/Bio/Models/Domain.php` (new)
|
||||
- `app/Mod/Bio/Http/Controllers/DomainController.php` (new)
|
||||
- `database/migrations/2026_01_16_create_domains_table.php` (new)
|
||||
- `app/Mod/Bio/Tests/Feature/DomainTest.php` (new)
|
||||
```
|
||||
|
||||
### 4. "What Was Built" Summary
|
||||
```markdown
|
||||
**Summary:** Custom domain management with DNS verification. Users can add domains,
|
||||
system generates TXT record for verification, background job checks DNS propagation.
|
||||
Includes SSL provisioning via Caddy API.
|
||||
```
|
||||
|
||||
### Why This Matters
|
||||
|
||||
In Jan 2026, an audit found:
|
||||
- Commerce Matrix Plan marked "95% done" was actually 75%
|
||||
- Internal WAF section was skipped entirely (extracted to Core Bouncer)
|
||||
- Warehouse/fulfillment (6 features) listed as "one item" in TODO
|
||||
- Task files read like planning documents, not completion logs
|
||||
|
||||
**Without evidence, "done" means nothing.**
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Task Creation
|
||||
|
||||
Human or planning agent creates task file in `tasks/`:
|
||||
- Status: `draft`
|
||||
- Must have clear acceptance criteria
|
||||
- Must have implementation checklist
|
||||
|
||||
### 2. Task Ready
|
||||
|
||||
Human reviews and sets:
|
||||
- Status: `ready`
|
||||
- Assignee: `next available agent`
|
||||
|
||||
### 3. Implementation
|
||||
|
||||
Implementation agent:
|
||||
- Sets status: `in_progress`
|
||||
- Works through implementation checklist
|
||||
- Checks boxes as work is done
|
||||
- When complete, sets status: `needs_verification`
|
||||
- **MUST NOT** mark acceptance criteria as passed
|
||||
|
||||
### 4. Verification
|
||||
|
||||
Different agent (verification agent):
|
||||
- Reads the task file
|
||||
- Independently checks each acceptance criterion
|
||||
- Records evidence in Verification Results section
|
||||
- Sets verdict: PASS or FAIL
|
||||
- If PASS: status → `verified`, move to `archive/released/`
|
||||
- If FAIL: status → `in_progress`, back to implementation agent
|
||||
|
||||
### 5. Human Approval
|
||||
|
||||
Human reviews verified task:
|
||||
- Spot-check the evidence
|
||||
- If satisfied: status → `approved`, can delete or keep in archive
|
||||
- If not: back to `needs_verification` with notes
|
||||
|
||||
---
|
||||
|
||||
## Agent Instructions
|
||||
|
||||
### For Implementation Agents
|
||||
|
||||
```
|
||||
You are implementing TASK-XXX.
|
||||
|
||||
1. Read the full task file
|
||||
2. Set status to "in_progress"
|
||||
3. Work through the implementation checklist
|
||||
4. Check boxes ONLY for work you have completed
|
||||
5. When done, set status to "needs_verification"
|
||||
6. DO NOT check acceptance criteria boxes
|
||||
7. DO NOT mark the task as complete
|
||||
8. Update "Last Updated" with current timestamp
|
||||
|
||||
Your job is to do the work, not to verify it.
|
||||
```
|
||||
|
||||
### For Verification Agents
|
||||
|
||||
```
|
||||
You are verifying TASK-XXX.
|
||||
|
||||
1. Read the full task file
|
||||
2. For EACH acceptance criterion:
|
||||
a. Check the codebase independently
|
||||
b. Record what you found (file paths, line numbers, test output)
|
||||
c. Mark as PASS, FAIL, or PARTIAL with evidence
|
||||
3. Add a new "Verification Results" section with today's date
|
||||
4. Set verdict: PASS or FAIL
|
||||
5. If PASS: move file to archive/released/
|
||||
6. If FAIL: set status back to "in_progress"
|
||||
7. Update "Last Updated" with current timestamp
|
||||
|
||||
You are the gatekeeper. Be thorough. Trust nothing the implementation agent said.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Status Flow
|
||||
|
||||
```
|
||||
draft → ready → in_progress → needs_verification → verified → approved
|
||||
↑ │
|
||||
└────────────────────┘
|
||||
(if verification fails)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase-Based Decomposition
|
||||
|
||||
Large tasks should be decomposed into independent phases that can be executed in parallel by multiple agents. This dramatically reduces implementation time.
|
||||
|
||||
### Phase Independence Rules
|
||||
|
||||
1. **No shared state** — Each phase writes to different files/tables
|
||||
2. **No blocking dependencies** — Phase 3 shouldn't wait for Phase 2's output
|
||||
3. **Clear boundaries** — Each phase has its own acceptance criteria
|
||||
4. **Testable isolation** — Phase tests don't require other phases
|
||||
|
||||
### Example Decomposition
|
||||
|
||||
A feature like "BioHost Missing Features" might decompose into:
|
||||
|
||||
| Phase | Focus | Can Parallel With |
|
||||
|-------|-------|-------------------|
|
||||
| 1 | Domain Management | 2, 3, 4 |
|
||||
| 2 | Project System | 1, 3, 4 |
|
||||
| 3 | Analytics Core | 1, 2, 4 |
|
||||
| 4 | Form Submissions | 1, 2, 3 |
|
||||
| 5 | Link Scheduling | 1, 2, 3, 4 |
|
||||
| ... | ... | ... |
|
||||
| 12 | MCP Tools (polish) | After 1-11 |
|
||||
| 13 | Admin UI (polish) | After 1-11 |
|
||||
|
||||
### Phase Sizing
|
||||
|
||||
- **Target**: 4-8 acceptance criteria per phase
|
||||
- **Estimated time**: 2-4 hours per phase
|
||||
- **Test count**: 15-40 tests per phase
|
||||
- **File count**: 3-10 files modified per phase
|
||||
|
||||
---
|
||||
|
||||
## Standard Phase Types
|
||||
|
||||
Every large task should include these phase types:
|
||||
|
||||
### Core Implementation Phases (1-N)
|
||||
|
||||
The main feature work. Group by:
|
||||
- **Resource type** (domains, projects, analytics)
|
||||
- **Functional area** (CRUD, scheduling, notifications)
|
||||
- **Data flow** (input, processing, output)
|
||||
|
||||
### Polish Phase: MCP Tools
|
||||
|
||||
**Always include as second-to-last phase.**
|
||||
|
||||
Exposes all implemented features to AI agents via MCP protocol.
|
||||
|
||||
Standard acceptance criteria:
|
||||
- [ ] MCP tool class exists at `app/Mcp/Tools/{Feature}Tools.php`
|
||||
- [ ] All CRUD operations exposed as actions
|
||||
- [ ] Tool includes prompts for common workflows
|
||||
- [ ] Tool includes resources for data access
|
||||
- [ ] Tests verify all MCP actions return expected responses
|
||||
- [ ] Tool registered in MCP service provider
|
||||
|
||||
### Polish Phase: Admin UI Integration
|
||||
|
||||
**Always include as final phase.**
|
||||
|
||||
Integrates features into the admin dashboard.
|
||||
|
||||
Standard acceptance criteria:
|
||||
- [ ] Sidebar navigation updated with feature section
|
||||
- [ ] Index/list page with filtering and search
|
||||
- [ ] Detail/edit pages for resources
|
||||
- [ ] Bulk actions where appropriate
|
||||
- [ ] Breadcrumb navigation
|
||||
- [ ] Role-based access control
|
||||
- [ ] Tests verify all admin routes respond correctly
|
||||
|
||||
---
|
||||
|
||||
## Parallel Agent Execution
|
||||
|
||||
### Firing Multiple Agents
|
||||
|
||||
When phases are independent, fire agents simultaneously:
|
||||
|
||||
```
|
||||
Human: "Implement phases 1-4 in parallel"
|
||||
|
||||
Agent fires 4 Task tools simultaneously:
|
||||
- Task(Phase 1: Domain Management)
|
||||
- Task(Phase 2: Project System)
|
||||
- Task(Phase 3: Analytics Core)
|
||||
- Task(Phase 4: Form Submissions)
|
||||
```
|
||||
|
||||
### Agent Prompt Template
|
||||
|
||||
```
|
||||
You are implementing Phase X of TASK-XXX: [Task Title]
|
||||
|
||||
Read the task file at: tasks/TASK-XXX-feature-name.md
|
||||
|
||||
Your phase covers acceptance criteria ACxx through ACyy.
|
||||
|
||||
Implementation requirements:
|
||||
1. Create all files listed in the Phase X implementation checklist
|
||||
2. Write comprehensive Pest tests (target: 20-40 tests)
|
||||
3. Follow existing codebase patterns
|
||||
4. Use workspace-scoped multi-tenancy
|
||||
5. Check entitlements for tier-gated features
|
||||
|
||||
When complete:
|
||||
1. Update the task file marking Phase X checklist items done
|
||||
2. Report: files created, test count, any blockers
|
||||
|
||||
Do NOT mark acceptance criteria as passed — verification agent does that.
|
||||
```
|
||||
|
||||
### Coordination Rules
|
||||
|
||||
1. **Linter accepts all** — Configure to auto-accept agent file modifications
|
||||
2. **No merge conflicts** — Phases write to different files
|
||||
3. **Collect results** — Wait for all agents, then fire next wave
|
||||
4. **Wave pattern** — Group dependent phases into waves
|
||||
|
||||
### Wave Execution Example
|
||||
|
||||
```
|
||||
Wave 1 (parallel): Phases 1, 2, 3, 4
|
||||
↓ (all complete)
|
||||
Wave 2 (parallel): Phases 5, 6, 7, 8
|
||||
↓ (all complete)
|
||||
Wave 3 (parallel): Phases 9, 10, 11
|
||||
↓ (all complete)
|
||||
Wave 4 (sequential): Phase 12 (MCP), then Phase 13 (UI)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task File Schema (Extended)
|
||||
|
||||
For large phased tasks, extend the schema:
|
||||
|
||||
```markdown
|
||||
# TASK-XXX: [Feature Name]
|
||||
|
||||
**Status:** draft | ready | in_progress | needs_verification | verified | approved
|
||||
**Created:** YYYY-MM-DD
|
||||
**Last Updated:** YYYY-MM-DD HH:MM by [agent/human]
|
||||
**Complexity:** small (1-3 phases) | medium (4-8 phases) | large (9+ phases)
|
||||
**Estimated Phases:** N
|
||||
**Completed Phases:** M/N
|
||||
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
[One paragraph: what does "done" look like?]
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
- **Models:** X new, Y modified
|
||||
- **Migrations:** Z new tables
|
||||
- **Livewire Components:** A new
|
||||
- **Tests:** B target test count
|
||||
- **Estimated Hours:** C-D hours
|
||||
|
||||
---
|
||||
|
||||
## Phase Overview
|
||||
|
||||
| Phase | Name | Status | ACs | Tests |
|
||||
|-------|------|--------|-----|-------|
|
||||
| 1 | Domain Management | ✅ Done | AC1-5 | 28 |
|
||||
| 2 | Project System | ✅ Done | AC6-10 | 32 |
|
||||
| 3 | Analytics Core | 🔄 In Progress | AC11-16 | - |
|
||||
| ... | ... | ... | ... | ... |
|
||||
| 12 | MCP Tools | ⏳ Pending | AC47-53 | - |
|
||||
| 13 | Admin UI | ⏳ Pending | AC54-61 | - |
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
### Phase 1: Domain Management
|
||||
|
||||
- [ ] AC1: [Criterion]
|
||||
- [ ] AC2: [Criterion]
|
||||
...
|
||||
|
||||
### Phase 12: MCP Tools (Standard)
|
||||
|
||||
- [ ] AC47: MCP tool class exists with all feature actions
|
||||
- [ ] AC48: CRUD operations for all resources exposed
|
||||
- [ ] AC49: Bulk operations exposed (where applicable)
|
||||
- [ ] AC50: Query/filter operations exposed
|
||||
- [ ] AC51: MCP prompts created for common workflows
|
||||
- [ ] AC52: MCP resources expose read-only data access
|
||||
- [ ] AC53: Tests verify all MCP actions
|
||||
|
||||
### Phase 13: Admin UI Integration (Standard)
|
||||
|
||||
- [ ] AC54: Sidebar updated with feature navigation
|
||||
- [ ] AC55: Feature has expandable submenu (if 3+ pages)
|
||||
- [ ] AC56: Index pages with DataTable/filtering
|
||||
- [ ] AC57: Create/Edit forms with validation
|
||||
- [ ] AC58: Detail views with related data
|
||||
- [ ] AC59: Bulk action support
|
||||
- [ ] AC60: Breadcrumb navigation
|
||||
- [ ] AC61: Role-based visibility
|
||||
|
||||
---
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Phase 1: Domain Management
|
||||
- [ ] File: `app/Models/...`
|
||||
- [ ] File: `app/Livewire/...`
|
||||
- [ ] Test: `tests/Feature/...`
|
||||
|
||||
### Phase 12: MCP Tools
|
||||
- [ ] File: `app/Mcp/Tools/{Feature}Tools.php`
|
||||
- [ ] File: `app/Mcp/Prompts/{Feature}Prompts.php` (optional)
|
||||
- [ ] File: `app/Mcp/Resources/{Feature}Resources.php` (optional)
|
||||
- [ ] Test: `tests/Feature/Mcp/{Feature}ToolsTest.php`
|
||||
|
||||
### Phase 13: Admin UI
|
||||
- [ ] File: `resources/views/admin/components/sidebar.blade.php` (update)
|
||||
- [ ] File: `app/Livewire/Admin/{Feature}/Index.php`
|
||||
- [ ] File: `resources/views/livewire/admin/{feature}/index.blade.php`
|
||||
- [ ] Test: `tests/Feature/Admin/{Feature}Test.php`
|
||||
|
||||
---
|
||||
|
||||
## Verification Results
|
||||
|
||||
[Same as before]
|
||||
|
||||
---
|
||||
|
||||
## Phase Completion Log
|
||||
|
||||
### Phase 1: Domain Management
|
||||
**Completed:** YYYY-MM-DD by [Agent ID]
|
||||
**Tests:** 28 passing
|
||||
**Files:** 8 created/modified
|
||||
**Notes:** [Any context]
|
||||
|
||||
### Phase 2: Project System
|
||||
**Completed:** YYYY-MM-DD by [Agent ID]
|
||||
**Tests:** 32 passing
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP Endpoint (Future)
|
||||
|
||||
When implemented, the MCP endpoint will expose:
|
||||
|
||||
```
|
||||
GET /tasks # List all tasks with status
|
||||
GET /tasks/{id} # Get task details
|
||||
POST /tasks/{id}/claim # Agent claims a task
|
||||
POST /tasks/{id}/complete # Agent marks ready for verification
|
||||
POST /tasks/{id}/verify # Verification agent submits results
|
||||
GET /tasks/next # Get next unclaimed task
|
||||
GET /tasks/verify-queue # Get tasks needing verification
|
||||
POST /tasks/{id}/phases/{n}/claim # Claim specific phase
|
||||
POST /tasks/{id}/phases/{n}/complete # Complete specific phase
|
||||
GET /tasks/{id}/phases # List phase status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Metrics to Track
|
||||
|
||||
- Tasks created vs completed (per week)
|
||||
- Verification pass rate on first attempt
|
||||
- Average time from ready → approved
|
||||
- Most common failure reasons
|
||||
|
||||
---
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
When a feature applies to multiple modules, extract it.
|
||||
|
||||
### Example: Core Bouncer
|
||||
|
||||
The Commerce Matrix Plan included an "Internal WAF" section — a request whitelisting system with training mode. During audit, we realised:
|
||||
|
||||
- It's not commerce-specific
|
||||
- It applies to all admin routes, all API endpoints
|
||||
- It should be in `Core/`, not `Commerce/`
|
||||
|
||||
**Action:** Extracted to `CORE_BOUNCER_PLAN.md` as a framework-level concern.
|
||||
|
||||
### Signs to Extract
|
||||
|
||||
- Feature name doesn't include the module name naturally
|
||||
- You'd copy-paste it to other modules
|
||||
- It's about infrastructure, not business logic
|
||||
- Multiple modules would benefit independently
|
||||
|
||||
### How to Extract
|
||||
|
||||
1. Create new task file for the cross-cutting concern
|
||||
2. Add note to original plan: `> **EXTRACTED:** Section moved to X`
|
||||
3. Update TODO.md with the new task
|
||||
4. Don't delete from original — leave the note for context
|
||||
|
||||
---
|
||||
|
||||
## Retrospective Audits
|
||||
|
||||
Periodically audit archived tasks against actual implementation.
|
||||
|
||||
### When to Audit
|
||||
|
||||
- Before starting dependent work
|
||||
- When resuming a project after a break
|
||||
- When something "complete" seems broken
|
||||
- Monthly for active projects
|
||||
|
||||
### Audit Process
|
||||
|
||||
1. Read the archived task file
|
||||
2. Check each acceptance criterion against codebase
|
||||
3. Run the tests mentioned in the task
|
||||
4. Document gaps found
|
||||
|
||||
### Audit Template
|
||||
|
||||
```markdown
|
||||
## Audit: TASK-XXX
|
||||
**Date:** YYYY-MM-DD
|
||||
**Auditor:** [human/agent]
|
||||
|
||||
| Claimed | Actual | Gap |
|
||||
|---------|--------|-----|
|
||||
| Phase 1 complete | ✅ Verified | None |
|
||||
| Phase 2 complete | ⚠️ Partial | Missing X service |
|
||||
| Phase 3 complete | ❌ Not done | Only stubs exist |
|
||||
|
||||
**Action items:**
|
||||
- [ ] Create TASK-YYY for Phase 2 gap
|
||||
- [ ] Move Phase 3 back to TODO as incomplete
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
### General
|
||||
|
||||
1. **Same agent implements and verifies** — defeats the purpose
|
||||
2. **Vague acceptance criteria** — "it works" is not verifiable
|
||||
3. **Skipping verification** — the whole point is independent checking
|
||||
4. **Bulk marking as done** — verify one task at a time
|
||||
5. **Human approving without spot-check** — trust but verify
|
||||
|
||||
### Evidence & Documentation
|
||||
|
||||
6. **Checklist without evidence** — planning ≠ implementation
|
||||
7. **Skipping "What Was Built" summary** — context lost on compaction
|
||||
8. **No test count** — can't verify without knowing what to run
|
||||
9. **Marking section "done" without implementation** — major gaps discovered in audits
|
||||
10. **Vague TODO items** — "Warehouse system" hides 6 distinct features
|
||||
|
||||
### Parallel Execution
|
||||
|
||||
11. **Phases with shared files** — causes merge conflicts
|
||||
12. **Sequential dependencies in same wave** — blocks parallelism
|
||||
13. **Skipping polish phases** — features hidden from agents and admins
|
||||
14. **Too many phases per wave** — diminishing returns past 4-5 agents
|
||||
15. **No wave boundaries** — chaos when phases actually do depend
|
||||
|
||||
### MCP Tools
|
||||
|
||||
16. **Exposing without testing** — broken tools waste agent time
|
||||
17. **Missing bulk operations** — agents do N calls instead of 1
|
||||
18. **No error context** — agents can't debug failures
|
||||
|
||||
### Admin UI
|
||||
|
||||
19. **Flat navigation for large features** — use expandable submenus
|
||||
20. **Missing breadcrumbs** — users get lost
|
||||
21. **No bulk actions** — tedious admin experience
|
||||
|
||||
### Cross-Cutting Concerns
|
||||
|
||||
22. **Burying framework features in module plans** — extract them
|
||||
23. **Assuming module-specific when it's not** — ask "would other modules need this?"
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Creating a New Task
|
||||
|
||||
1. Copy the extended schema template
|
||||
2. Fill in objective and scope
|
||||
3. Decompose into phases (aim for 4-8 ACs each)
|
||||
4. Map phase dependencies → wave structure
|
||||
5. Check for cross-cutting concerns — extract if needed
|
||||
6. **Always add Phase N-1: MCP Tools**
|
||||
7. **Always add Phase N: Admin UI Integration**
|
||||
8. Set status to `draft`, get human review
|
||||
9. When `ready`, fire Wave 1 agents in parallel
|
||||
10. Collect results with evidence (commits, tests, files)
|
||||
11. Fire next wave
|
||||
12. After all phases, run verification agent
|
||||
13. Human approval → move to `archive/released/`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Completing a Phase
|
||||
|
||||
1. Do the work
|
||||
2. Run the tests
|
||||
3. Record evidence:
|
||||
- Git commits (hashes + messages)
|
||||
- Test count and command to run them
|
||||
- Files created/modified
|
||||
- "What Was Built" summary (2-3 sentences)
|
||||
4. Update task file with Phase Completion Log entry
|
||||
5. Set phase status to ✅ Done
|
||||
6. Move to next phase or request verification
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Auditing Archived Work
|
||||
|
||||
1. Read `archive/released/` task file
|
||||
2. For each phase marked complete:
|
||||
- Check files exist
|
||||
- Run listed tests
|
||||
- Verify against acceptance criteria
|
||||
3. Document gaps using Audit Template
|
||||
4. Create new tasks for missing work
|
||||
5. Update TODO.md with accurate status
|
||||
|
||||
---
|
||||
|
||||
*This protocol exists because agents lie (unintentionally). The system catches the lies. Parallel execution makes them lie faster, so we verify more. Evidence requirements ensure lies are caught before archiving.*
|
||||
9
.idea/go-io.iml
generated
Normal file
9
.idea/go-io.iml
generated
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KubernetesApiProvider">{}</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/go-io.iml" filepath="$PROJECT_DIR$/.idea/go-io.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/workspace.xml
generated
Normal file
8
.idea/workspace.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"settings.editor.selected.configurable": "vcs.Git"
|
||||
}
|
||||
}</component>
|
||||
</project>
|
||||
|
|
@ -251,8 +251,8 @@ func TestCopy_Bad(t *testing.T) {
|
|||
// --- Local Global Tests ---
|
||||
|
||||
func TestLocalGlobal_Good(t *testing.T) {
|
||||
// io.Local should be initialized by init()
|
||||
assert.NotNil(t, Local, "io.Local should be initialized")
|
||||
// io.Local should be initialised by init()
|
||||
assert.NotNil(t, Local, "io.Local should be initialised")
|
||||
|
||||
// Should be able to use it as a Medium
|
||||
var m = Local
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ func (m *Medium) DataNode() *datanode.DataNode {
|
|||
return m.dn
|
||||
}
|
||||
|
||||
// clean normalizes a path: strips leading slash, cleans traversal.
|
||||
// clean normalises a path: strips leading slash, cleans traversal.
|
||||
func clean(p string) string {
|
||||
p = strings.TrimPrefix(p, "/")
|
||||
p = path.Clean(p)
|
||||
|
|
|
|||
34
go.mod
34
go.mod
|
|
@ -5,25 +5,25 @@ go 1.26.0
|
|||
require (
|
||||
forge.lthn.ai/Snider/Borg v0.3.1
|
||||
forge.lthn.ai/core/go v0.3.0
|
||||
forge.lthn.ai/core/go-crypt v0.1.5
|
||||
forge.lthn.ai/core/go-log v0.0.1
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.3
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4
|
||||
forge.lthn.ai/core/go-crypt v0.1.6
|
||||
forge.lthn.ai/core/go-log v0.0.2
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/crypto v0.49.0
|
||||
modernc.org/sqlite v1.46.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
|
|
@ -34,10 +34,10 @@ require (
|
|||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.68.0 // indirect
|
||||
modernc.org/libc v1.70.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
|
|
|||
39
go.sum
39
go.sum
|
|
@ -4,30 +4,56 @@ forge.lthn.ai/core/go v0.3.0 h1:mOG97ApMprwx9Ked62FdWVwXTGSF6JO6m0DrVpoH2Q4=
|
|||
forge.lthn.ai/core/go v0.3.0/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc=
|
||||
forge.lthn.ai/core/go-crypt v0.1.5 h1:eqrKrdZPpg9q2GQBKNTkGMOlEg+8X/YE3KpVwdvbvww=
|
||||
forge.lthn.ai/core/go-crypt v0.1.5/go.mod h1:1MeNMvKy6EtXf6J8IVUlcKVlUBwPL4E3lwTy6qg5SEM=
|
||||
forge.lthn.ai/core/go-crypt v0.1.6 h1:jB7L/28S1NR+91u3GcOYuKfBLzPhhBUY1fRe6WkGVns=
|
||||
forge.lthn.ai/core/go-crypt v0.1.6/go.mod h1:4VZAGqxlbadhSB66sJkdj54/HSJ+bSxVgwWK5kMMYDo=
|
||||
forge.lthn.ai/core/go-log v0.0.1 h1:x/E6EfF9vixzqiLHQOl2KT25HyBcMc9qiBkomqVlpPg=
|
||||
forge.lthn.ai/core/go-log v0.0.1/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
|
||||
forge.lthn.ai/core/go-log v0.0.2 h1:zJEgbajs5AjvhYzsbyybzn+S2Titiv56r3BG5E1cNUo=
|
||||
forge.lthn.ai/core/go-log v0.0.2/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ=
|
||||
github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.3/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 h1:N4lRUXZpZ1KVEUn6hxtco/1d2lgYhNn1fHkkl8WhlyQ=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 h1:3kGOqnh1pPeddVa/E37XNTaWJ8W6vrbYV9lJEkCnhuY=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 h1:/sECfyq2JTifMI2JPyZ4bdRN77zJmr6SrS1eL3augIA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19/go.mod h1:dMf8A5oAqr9/oxOfLkC/c2LU/uMcALP0Rgn2BD5LWn0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 h1:AWeJMk33GTBf6J20XJe6qZoRSJo0WfUhsMdUKhoODXE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19/go.mod h1:+GWrYoaAsV7/4pNHpwh1kiNLXkKaSoppxQq9lbH8Ejw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.20 h1:qi3e/dmpdONhj1RyIZdi6DKKpDXS5Lb8ftr3p7cyHJc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.20/go.mod h1:V1K+TeJVD5JOk3D9e5tsX2KUdL7BlB+FV6cBhdobN8c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21 h1:SwGMTMLIlvDNyhMteQ6r8IJSBPlRdXX5d4idhIGbkXA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21/go.mod h1:UUxgWxofmOdAMuqEsSppbDtGKLfR04HGsD0HXzvhI1k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 h1:XAq62tBTJP/85lFD5oqOOe7YYgWxY9LvWq8plyDvDVg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 h1:BYf7XNsJMzl4mObARUBUib+j2tf0U//JAAtTnYqvqCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11/go.mod h1:aEUS4WrNk/+FxkBZZa7tVgp4pGH+kFGW40Y8rCPqt5g=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12 h1:qtJZ70afD3ISKWnoX3xB0J2otEqu3LqicRcDBqsj0hQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12/go.mod h1:v2pNpJbRNl4vEUWEh5ytQok0zACAKfdmKS51Hotc3pQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7suZk9UCJHE1Iw9GMZJJl0dAnKXXP1NaSDHwmw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20 h1:siU1A6xjUZ2N8zjTHSXFhB9L/2OY8Dqs0xXiLjF30jA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20/go.mod h1:4TLZCmVJDM3FOu5P5TJP0zOlu9zWgDWU7aUxWbr+rcw=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4 h1:4ExZyubQ6LQQVuF2Qp9OsfEvsTdAWh5Gfwf6PgIdLdk=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1 h1:csi9NLpFZXb9fxY7rS1xVzgPRGMt7MSNWeQ6eo247kE=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1/go.mod h1:qXVal5H0ChqXP63t6jze5LmFalc7+ZE7wOdLtZ0LCP0=
|
||||
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
|
|
@ -61,17 +87,26 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
|||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
|
@ -81,8 +116,10 @@ modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
|||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.30.2 h1:4yPaaq9dXYXZ2V8s1UgrC3KIj580l2N4ClrLwnbv2so=
|
||||
modernc.org/ccgo/v4 v4.30.2/go.mod h1:yZMnhWEdW0qw3EtCndG1+ldRrVGS+bIwyWmAWzS0XEw=
|
||||
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||
|
|
@ -91,6 +128,8 @@ modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
|||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ=
|
||||
modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0=
|
||||
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
|
||||
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
|
|
|
|||
4
io.go
4
io.go
|
|
@ -102,7 +102,7 @@ func (de DirEntry) IsDir() bool { return de.isDir }
|
|||
func (de DirEntry) Type() fs.FileMode { return de.mode.Type() }
|
||||
func (de DirEntry) Info() (fs.FileInfo, error) { return de.info, nil }
|
||||
|
||||
// Local is a pre-initialized medium for the local filesystem.
|
||||
// Local is a pre-initialised medium for the local filesystem.
|
||||
// It uses "/" as root, providing unsandboxed access to the filesystem.
|
||||
// For sandboxed access, use NewSandboxed with a specific root path.
|
||||
var Local Medium
|
||||
|
|
@ -111,7 +111,7 @@ func init() {
|
|||
var err error
|
||||
Local, err = local.New("/")
|
||||
if err != nil {
|
||||
coreerr.Warn("io: failed to initialize Local medium, io.Local will be nil", "error", err)
|
||||
coreerr.Warn("io: failed to initialise Local medium, io.Local will be nil", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func New(root string) (*Medium, error) {
|
|||
return &Medium{root: abs}, nil
|
||||
}
|
||||
|
||||
// path sanitizes and returns the full path.
|
||||
// path sanitises and returns the full path.
|
||||
// Absolute paths are sandboxed under root (unless root is "/").
|
||||
func (m *Medium) path(p string) string {
|
||||
if p == "" {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func TestPath(t *testing.T) {
|
|||
// Empty returns root
|
||||
assert.Equal(t, "/home/user", m.path(""))
|
||||
|
||||
// Traversal attempts get sanitized
|
||||
// Traversal attempts get sanitised
|
||||
assert.Equal(t, "/home/user/file.txt", m.path("../file.txt"))
|
||||
assert.Equal(t, "/home/user/file.txt", m.path("dir/../file.txt"))
|
||||
|
||||
|
|
@ -279,7 +279,7 @@ func TestRename_Good(t *testing.T) {
|
|||
assert.Equal(t, "content", content)
|
||||
}
|
||||
|
||||
func TestRename_Traversal_Sanitized(t *testing.T) {
|
||||
func TestRename_Traversal_Sanitised(t *testing.T) {
|
||||
testRoot, err := os.MkdirTemp("", "local_rename_traversal_test")
|
||||
assert.NoError(t, err)
|
||||
defer func() { _ = os.RemoveAll(testRoot) }()
|
||||
|
|
@ -290,7 +290,7 @@ func TestRename_Traversal_Sanitized(t *testing.T) {
|
|||
err = medium.Write("file.txt", "content")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Traversal attempts are sanitized (.. becomes .), so this renames to "./escaped.txt"
|
||||
// Traversal attempts are sanitised (.. becomes .), so this renames to "./escaped.txt"
|
||||
// which is just "escaped.txt" in the root
|
||||
err = medium.Rename("file.txt", "../escaped.txt")
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -456,7 +456,7 @@ func TestValidatePath_Security(t *testing.T) {
|
|||
|
||||
// Test 1: Simple traversal
|
||||
_, err = m.validatePath("../outside.txt")
|
||||
assert.NoError(t, err) // path() sanitizes to root, so this shouldn't escape
|
||||
assert.NoError(t, err) // path() sanitises to root, so this shouldn't escape
|
||||
|
||||
// Test 2: Symlink escape
|
||||
// Create a symlink inside the sandbox pointing outside
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
func TestNew_Good(t *testing.T) {
|
||||
n := New()
|
||||
require.NotNil(t, n, "New() must not return nil")
|
||||
assert.NotNil(t, n.files, "New() must initialize the files map")
|
||||
assert.NotNil(t, n.files, "New() must initialise the files map")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func (m *Medium) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// cleanPath normalizes a path for consistent storage.
|
||||
// cleanPath normalises a path for consistent storage.
|
||||
// Uses a leading "/" before Clean to sandbox traversal attempts.
|
||||
func cleanPath(p string) string {
|
||||
clean := path.Clean("/" + p)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue