Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,24 @@ jobs:
go-version-file: go.mod
cache: true

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Go vet
run: go vet ./...

- name: Go tests
run: go test -count=1 ./...

- name: Validate schemas
run: jq empty schema/*.json internal/testfixtures/oss-adapter-contract/*.json
run: |
jq empty schema/*.json internal/testfixtures/oss-adapter-contract/*.json
jq empty harness/contracts/orient-bundle.schema.json

- name: Harness orient smoke
run: scripts/harness-orient-smoke.sh

- name: Diff whitespace check
run: git diff --check
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
.DS_Store
/bin/
/dist/
viewer/dist/
**/orient-smoke/
.cursor/
/.portolan/
/coverage.out
*.out
Expand Down
24 changes: 19 additions & 5 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,21 @@ be verified through link, schema, and placeholder checks instead of code tests.

## Product Constraints

- Primary implementation language: Go.
- Runtime default: local CLI.
- Default output: machine-readable evidence graph plus optional human-readable
packet generated from the same graph.
- Primary delivery: harness artifacts (`harness/` skills, recipes, guardrails,
contracts) plus optional local orient viewer output.
- Implementation language: Go remains for the legacy CLI and normalization
library; new product slices may use shell, TypeScript, or other local tooling
when a spec documents the boundary.
- Runtime default: harness-first workflow (OSS recipes → orient bundle → viewer);
legacy Go CLI is frozen for new features until the Go decision gate resolves.
- Default output: ranked `orient/` hotspot bundle for navigation; legacy
machine-readable evidence graph remains supported as an optional bridge.
- Local viewer: a read-only static orient viewer may be served locally for the
duration of a user session; it must not mutate targets or require network by
default.
- Viewer truth boundary: graph nodes in the orient viewer must come from imported
producer evidence or Portolan normalization; LLM-authored graphs are UX-only and
must never be labeled `source-visible`.
- Default privacy posture: no raw private source snippets, prompts, credentials,
provider URLs, or customer-sensitive payloads in committed fixtures.
- Default integration posture: import files and exported tool outputs before
Expand Down Expand Up @@ -81,4 +92,7 @@ Changes require:
- a migration note if existing specs become stale;
- fresh verification using the baseline checks in `AGENTS.md`.

**Version**: 1.0.0 | **Ratified**: 2026-05-20 | **Last Amended**: 2026-05-20
**Version**: 1.1.0 | **Ratified**: 2026-05-20 | **Last Amended**: 2026-06-10

Migration note (1.1.0): harness-first pivot per spec 087. Existing Go CLI specs
remain valid as legacy bridges; new MVP work targets `harness/` and `viewer/`.
28 changes: 23 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ Portolan is not:
Portolan is:

- a read-only local discovery substrate an agent can run;
- an agent-facing toolbox exposed through CLI first, then skills/MCP/LSP-style
surfaces when justified;
- a harness-first supplement (`harness/SKILL.md`, recipes, guardrails, orient
viewer) with an optional legacy Go CLI bridge;
- a normalizer for source, metadata, runtime, and claim evidence;
- a machine-readable evidence graph;
- a finding generator for relationships, duplication, configuration surfaces,
Expand All @@ -42,8 +42,11 @@ Portolan is:

## Engineering Rules

- Primary implementation language: Go.
- Keep `cmd/portolan` thin; put behavior in internal packages.
- Primary delivery: harness artifacts and orient bundle contract; see spec 087.
- Go CLI is frozen for new features per
[`docs/harness/GO-FREEZE-POLICY.md`](docs/harness/GO-FREEZE-POLICY.md).
- Keep `cmd/portolan` thin; put behavior in internal packages when Go changes are
allowed (bugfix/bridge only during freeze).
- Add focused tests before behavior changes.
- Do not add dependencies unless the product boundary and integration cost are
documented.
Expand Down Expand Up @@ -190,6 +193,9 @@ Run:
go test ./...
go vet ./...
jq empty schema/*.json
jq empty harness/contracts/orient-bundle.schema.json
scripts/harness-orient-smoke.sh
scripts/orient-wizard.sh --help
git diff --check
```

Expand All @@ -202,5 +208,17 @@ go run ./cmd/portolan scan --help
<!-- SPECKIT START -->
For additional context about technologies to be used, project structure,
shell commands, and other important information, read the current plan:
`docs/specs/083-tool-acquisition-guidance/plan.md`
`docs/specs/087-harness-first-product/plan.md`
<!-- SPECKIT END -->

## Learned User Preferences

- Ask clarifying questions before drafting implementation plans when harness targets, Go role, or MVP surface are still open.
- Prioritize user-facing landscape navigation (hotspots, tech debt, duplication, where to look first) over evidence-discipline artifacts as the primary deliverable.
- Treat B2B evidence guardrails as a secondary layer on top of the map, not the main reason to use Portolan.
- Expand harness rules, guardrails, and OSS tool recipes rather than the Go codebase when adding product behavior.

## Learned Workspace Facts

- Understand-Anything is the reference UX for the local orient map; graph nodes must stay evidence-backed, not LLM-authored truth.
- Product-success questions center on concrete code pain (debt, duplication, risky zones), not abstract trust infrastructure alone.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,31 @@ Use Portolan when you want an agent to answer questions like:
Portolan is especially useful when the target is messy, multi-repo, legacy, or
partly black-box.

## What You Get
## Harness-First Quick Start (recommended)

The main workflow creates a context pack for an agent:
Portolan is primarily a **harness supplement**: portable skill, OSS recipes,
guardrails, and a local orient map — not a Go module you must install first.

**One command** (tool check → recipes → bundle → viewer):

```bash
scripts/orient-wizard.sh <target-root> <orient-dir> --yes
```

Add `--no-viewer` to build only. See `scripts/orient-wizard.sh --help`.

The orient viewer supports search, filters, a directory heat map, and click-to-source
preview (local files only). See [`docs/demo-runbook.md`](docs/demo-runbook.md).

Manual fallback: read [`harness/SKILL.md`](harness/SKILL.md), run recipes from
[`harness/recipes/`](harness/recipes/), then `scripts/build-orient-bundle.sh`.

See [`docs/harness/GO-FREEZE-POLICY.md`](docs/harness/GO-FREEZE-POLICY.md) for
legacy Go CLI status.

## What You Get (legacy Go path)

The Go CLI workflow creates a context pack for an agent:

```bash
portolan context prepare --root <target-root> --out <output-dir>/context --profile agent
Expand Down Expand Up @@ -277,5 +299,7 @@ For repository development:
```bash
go test -count=1 ./...
jq empty schema/*.json
jq empty harness/contracts/orient-bundle.schema.json
scripts/harness-orient-smoke.sh
git diff --check
```
41 changes: 41 additions & 0 deletions docs/adr/001-go-cli-fate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ADR 001: Go CLI Fate (Decision Gate)

**Status**: Provisional — thin maintenance layer (2026-06-10)

**Context**: Harness pivot (spec 087) ships recipes, orient bundle, and viewer
without requiring Go. Phase 5 smoke validates harness-only path on fixtures.

## Decision gate criteria

| Criterion | Result (2026-06-10 smoke) |
| --- | --- |
| Harness + scripts build orient bundle | Pass (`build-orient-bundle.sh`) |
| Viewer loads bundle without Go | Pass (`harness-orient-smoke.sh`) |
| Importer edge cases in production | Not re-tested; legacy `internal/importer` retained |
| Large JSONL / path safety | Legacy Go still has tests; harness uses jq/bash |

## Decision

**Keep Go in maintenance mode** until harness importers cover production edge
cases or a dedicated spec deprecates `cmd/portolan`.

Allowed:

- `map`, `import`, `query`, `orient-export-from-map.sh` bridge
- Bugfixes and security fixes per GO-FREEZE-POLICY

Not allowed without new ADR:

- New product features in Go
- `contextprep` / answer-contract growth

## Consequences

- Primary docs and INSTALL-PROMPT point to `harness/SKILL.md`.
- Contributors add recipes and viewer features first.
- Revisit this ADR after real-target harness runs (non-fixture) or importer parity spec.

## Alternatives considered

1. **Deprecate Go immediately** — rejected; importer bridge still useful.
2. **Go as primary** — rejected; conflicts with harness-first product.
113 changes: 44 additions & 69 deletions docs/agent/INSTALL-PROMPT.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,59 @@
# Agent Install Prompt

Use this prompt when you want an AI agent to install and run Portolan without
hidden scaffolding.
Use this prompt when you want an AI agent to run Portolan without hidden
scaffolding.

Copy the prompt block below to the receiving agent. If an agent receives this
whole file instead of only the block, it should execute the prompt block after
the three variables are filled in, not ask what to do next.
**Recommended (harness-first):** see [`harness/SKILL.md`](../../harness/SKILL.md)
and harness-specific prompts:

Replace the three variables with absolute local paths:
- [`harness/opencode/INSTALL-PROMPT.md`](../../harness/opencode/INSTALL-PROMPT.md)
- [`harness/codex-claude/INSTALL-PROMPT.md`](../../harness/codex-claude/INSTALL-PROMPT.md)

Replace variables with absolute local paths:

```text
PORTOLAN_PATH=<absolute path to a Portolan checkout or installed portolan binary>
TARGET_PATH=<absolute path to the local codebase or landscape to inspect>
OUTPUT_PATH=<absolute path to an empty output directory>
PORTOLAN_PATH=<absolute path to Portolan checkout>
TARGET_PATH=<absolute path to local target>
ORIENT_PATH=<absolute path to empty orient output directory>
```

For OpenCode default-permission runs, prefer an `OUTPUT_PATH` inside the
Portolan checkout, for example
`<portolan-checkout>/.portolan/runs/<target-name>`. The recorded OpenCode
external-output default-permission lane failed when the harness rejected the
external output path.

Then send:

```text
Install and use Portolan for a local read-only codebase navigation pass.
Execute these steps now and report the result. Do not ask whether to execute
unless a required local path is missing.

Inputs:
- PORTOLAN_PATH=<absolute path to a Portolan checkout or installed portolan binary>
- TARGET_PATH=<absolute path to the local target>
- OUTPUT_PATH=<absolute path to an empty output directory>

Rules:
- Use only these local paths.
- Do not use network access, credentials, cloning, daemons, or target mutation
unless I explicitly approve it.
- If your harness rejects writes to OUTPUT_PATH, fall back once to a repo-local
`.portolan/runs/<target-name>` directory under the Portolan checkout and
record the original OUTPUT_PATH write as `failed`. Use that fallback
directory as OUTPUT_PATH for the remaining steps.
- If you are using OpenCode with default permissions, prefer that repo-local
`.portolan/runs/<target-name>` output path before attempting an external
output path.
- If PORTOLAN_PATH is a binary, verify it with `--version`.
- If PORTOLAN_PATH is a source checkout, follow `docs/agent/INSTALL.md` and
build the repo-local binary with `scripts/bootstrap-portolan`.
- Prepare context into `OUTPUT_PATH/context`.
- Build a map into `OUTPUT_PATH/map` when the target size is reasonable.
- If `TARGET_PATH/selection.json` exists, validate it and prefer
`map --selection TARGET_PATH/selection.json --out OUTPUT_PATH/map` for the
map step. Otherwise use `map --root TARGET_PATH --out OUTPUT_PATH/map`.
- If selection validation fails, record that command as `failed`, then fall
back to `map --root TARGET_PATH --out OUTPUT_PATH/map`.
- Read bounded artifacts before opening large graph files:
- `context/agent-brief.md`
- `context/answer-contract.md`
- `context/evidence-index.jsonl`
- `context/gaps.jsonl`
- `map/summary.json`
- `map/graph-index.json`
- `map/findings.jsonl`
- `map/map.md`
- Preserve `verified`, `failed`, `blocked`, `not_assessed`, `unknown`, and
`cannot_verify`.
- Cite local artifact paths for every material claim.
- Do not claim complete estate coverage, runtime topology, OSS scanner value,
or architecture facts unless the local Portolan artifacts prove them.
Run the Portolan orient harness on TARGET_PATH. Write the orient bundle to
ORIENT_PATH. Execute now; do not ask unless a path is missing.

1. Read PORTOLAN_PATH/harness/SKILL.md
2. PORTOLAN_PATH/scripts/orient-wizard.sh TARGET_PATH ORIENT_PATH --no-viewer --yes
(or run recipes manually + build-orient-bundle.sh if operator prefers)
3. Read hotspots.jsonl and gaps.jsonl; cite hotspot.id and producer_ref per
PORTOLAN_PATH/harness/guardrails/citation-rules.md

If harness blocks external writes, use
PORTOLAN_PATH/.portolan/runs/<target-name> as ORIENT_PATH.

Answer with:
1. Commands run and whether each was `verified`, `failed`, or `blocked`.
2. Artifact paths created.
3. Visible local scope and completeness limits.
4. Evidence-backed relationships, duplication, configuration surfaces, and
technical-debt candidates.
5. Explicit `unknown`, `cannot_verify`, and `not_assessed` surfaces.
6. Three useful next local actions.
7. Any unsupported claims you avoided or accidentally made.
1. Recipes run (verified/failed/blocked)
2. Top 5 hotspots by rank with evidence
3. Gaps and not_assessed surfaces
4. Viewer command: cd PORTOLAN_PATH/viewer && npm run serve -- --bundle ORIENT_PATH
5. Unsupported claims avoided

Legacy Go CLI (optional): docs/harness/GO-FREEZE-POLICY.md
```

For Russian-language agent runs, use
[`docs/agent/INSTALL-PROMPT.ru.md`](INSTALL-PROMPT.ru.md).
## Legacy Go install prompt

Use only when the operator explicitly needs `context prepare` / `map`:

```text
PORTOLAN_PATH=<checkout or binary>
TARGET_PATH=<target>
OUTPUT_PATH=<output>
```

Follow [`docs/agent/INSTALL.md`](INSTALL.md), bootstrap Go binary if needed,
run `context prepare` and `map` into OUTPUT_PATH. See git history or
`docs/product-claims.md` for artifact order.

For Russian-language runs, use [`INSTALL-PROMPT.ru.md`](INSTALL-PROMPT.ru.md).
59 changes: 59 additions & 0 deletions docs/demo-runbook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Portolan Demo Runbook

Live demo for a newcomer: **where code pain is** → **filter** → **directory tree** → **source**.

## Setup (two commands)

**Single repo (portolan):**

```bash
scripts/orient-wizard.sh . /tmp/orient-portolan --yes
```

**Bounded multi-repo (bigtop quick sample):**

```bash
scripts/orient-wizard.sh ~/projects/bigtop-landscape/repos /tmp/orient-bigtop \
--yes --limit-repos 3 --producers semgrep,syft
```

**Full landscape stress (18 repos, spec 091):**

```bash
scripts/orient-wizard.sh ~/projects/bigtop-landscape/repos /tmp/orient-bigtop-full \
--no-viewer --yes --shard-timeout 600 --jscpd-memory-mb 2048
```

Expect 30–90+ minutes. Failed shards appear in gaps (not a wizard abort).
Use viewer on the result: `node viewer/scripts/serve.js --bundle /tmp/orient-bigtop-full`

Wizard opens viewer automatically. For a fixed port:

```bash
cd viewer && node scripts/build-static.js
node scripts/serve.js --bundle /tmp/orient-portolan --port 4173
```

Open http://127.0.0.1:4173/

## 5-step demo script

1. **Context** — Point at header: target path, hotspot count, evidence-backed (not LLM graph).
2. **Search** — Type `TODO` or `duplicate`; list and tree narrow instantly.
3. **Filter** — Toggle `static-finding` or `duplication`; on bigtop, pick a **Repo** chip.
4. **Directory heat map** — Expand a hot folder (severity bar + count); click a hotspot row.
5. **Source** — In Detail, show file paths and the **Source** snippet from local files (read-only).

## Talking points

- Gaps/truncation banner = honest limits (`not_assessed`, budget cap).
- Every claim ties to `producer_ref` (jscpd, semgrep, syft).
- No network; local-first.

## Troubleshooting

| Issue | Fix |
| --- | --- |
| Empty viewer | Run `node viewer/scripts/build-static.js` first |
| No source snippet | Hotspot has no paths (e.g. dep-hub) — expected |
| Missing tools | Re-run wizard with `--yes` or install jscpd/semgrep/syft |
Loading
Loading