Document and verify independent output destinations
This commit is contained in:
@@ -799,6 +799,41 @@ func TestSubprocessStdoutAndSelectedReportDirectory(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessUsesIndependentTranscriptReportAndDebugDestinations(t *testing.T) {
|
||||
f := newProcessFixture(t, "normal", "[grammar]")
|
||||
transcriptDir := filepath.Join(f.dir, "transcripts")
|
||||
reportDir := filepath.Join(f.dir, "reports")
|
||||
debugDir := filepath.Join(f.dir, "debug")
|
||||
for _, dir := range []string{transcriptDir, reportDir} {
|
||||
if err := os.Mkdir(dir, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
f.output = filepath.Join(transcriptDir, "corrected.json")
|
||||
code, stdout, stderr := f.run(t, true, "--report-dir", reportDir, "--debug", "--work-dir", debugDir)
|
||||
if code != 0 || stdout != "" || !strings.HasPrefix(stderr, "Completed:") {
|
||||
t.Fatalf("code=%d stdout=%q stderr=%q", code, stdout, stderr)
|
||||
}
|
||||
if output, err := os.ReadFile(f.output); err != nil || !bytes.Contains(output, []byte("Hello world")) {
|
||||
t.Fatalf("transcript: %s %v", output, err)
|
||||
}
|
||||
for _, name := range []string{"errors.yml", "recoveries.yml", "report.md"} {
|
||||
if _, err := os.Stat(filepath.Join(reportDir, name)); err != nil {
|
||||
t.Fatalf("report %s: %v", name, err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(transcriptDir, name)); !os.IsNotExist(err) {
|
||||
t.Fatalf("report %s appeared beside transcript: %v", name, err)
|
||||
}
|
||||
}
|
||||
dirs, err := filepath.Glob(filepath.Join(debugDir, "run-*"))
|
||||
if err != nil || len(dirs) != 1 {
|
||||
t.Fatalf("debug runs: %v %v", dirs, err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dirs[0], "correction-ledger.json")); err != nil {
|
||||
t.Fatalf("debug capture: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessReportingAndDebugSelections(t *testing.T) {
|
||||
var expectedOutput, expectedSummary string
|
||||
for _, selection := range []struct{ reporting, debug bool }{
|
||||
@@ -1016,6 +1051,44 @@ func TestSubprocessInvalidInputLeavesFailedReports(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessInvalidInputWritesFailedReportsToSelectedDirectory(t *testing.T) {
|
||||
f := newProcessFixture(t, "normal", "[grammar]")
|
||||
if err := os.WriteFile(f.transcript, []byte("not JSON"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reportDir := filepath.Join(f.dir, "failed-reports")
|
||||
if err := os.Mkdir(reportDir, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, _, _ := f.run(t, true, "--report-dir", reportDir)
|
||||
if code != 1 || f.provider.callCount() != 0 {
|
||||
t.Fatalf("invalid input dispatched: code=%d calls=%d", code, f.provider.callCount())
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(reportDir, "errors.yml"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var document reporting.ErrorsDocument
|
||||
if err := yaml.Unmarshal(raw, &document); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if document.Status != reporting.StatusFailed || len(document.Errors) != 1 || document.Errors[0].Scope != "run" {
|
||||
t.Fatalf("failed report: %+v", document)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(reportDir, "recoveries.yml")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if raw, err := os.ReadFile(filepath.Join(reportDir, "report.md")); err != nil || !bytes.Contains(raw, []byte("Status: failed")) {
|
||||
t.Fatalf("Markdown status: %s %v", raw, err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(f.dir, "errors.yml")); !os.IsNotExist(err) {
|
||||
t.Fatalf("failed report appeared in default directory: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(f.output); !os.IsNotExist(err) {
|
||||
t.Fatalf("failed transcript published: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessOutputFailureBeforeModelDispatch(t *testing.T) {
|
||||
f := newProcessFixture(t, "normal", "[grammar]")
|
||||
f.output = filepath.Join(f.dir, "missing", "output.json")
|
||||
|
||||
17
docs/cli.md
17
docs/cli.md
@@ -48,12 +48,17 @@ validation. The shared setting flags are:
|
||||
An omitted or empty output value selects stdout. Nonempty output filenames are
|
||||
used literally, including whitespace.
|
||||
Reporting defaults to enabled. `--reporting=false` disables all three ordinary
|
||||
reports; an explicit false overrides a true file or overlay setting. With file
|
||||
output, reports go beside the transcript and `--report-dir` has no effect. With
|
||||
stdout output, `--report-dir` selects the report directory, defaulting to the
|
||||
invocation working directory. `--debug` separately enables retained detailed
|
||||
artifacts under `--work-dir`. Configuration inspection does not create or probe
|
||||
these output directories.
|
||||
reports; an explicit false overrides a true file or overlay setting. For stdout
|
||||
output, reports use the effective `reporting.stdout_dir`, which defaults to the
|
||||
invocation working directory. For file output, they go beside the transcript
|
||||
unless `--report-dir` is explicitly supplied. The flag selects the report
|
||||
directory for either transcript destination; a config-file or overlay value for
|
||||
`reporting.stdout_dir` alone affects stdout output only. A selected report
|
||||
directory must be nonblank when reporting is enabled. With reporting disabled,
|
||||
the report destination is inactive and is not probed. `--debug` separately
|
||||
enables retained detailed artifacts under `--work-dir`; selecting `--work-dir`
|
||||
alone does not enable capture. Configuration inspection does not create or
|
||||
probe these output directories.
|
||||
Per-role model, base URL, key, timeout, retry, and concurrency flags have been
|
||||
removed. Models and generation controls belong to PromptKit profiles, backend
|
||||
credentials and capacity to configuration, and retry/deadline settings to the
|
||||
|
||||
@@ -62,17 +62,20 @@ field provenance separately from ConfigKit's redacted settings YAML.
|
||||
| `thresholds.glossary`, `.homophones`, `.spoken_word`, `.grammar` | `0.8` each | Confidence thresholds in `[0, 1]`. |
|
||||
| `context.description` | Empty | Trimmed prompt context, at most 500 bytes. |
|
||||
| `reporting.enabled` | `true` | Write the ordinary three-file report bundle when processing. |
|
||||
| `reporting.stdout_dir` | `.` | Report directory for stdout transcript output; ignored for file output or disabled reporting. |
|
||||
| `reporting.stdout_dir` | `.` | Report directory for stdout transcript output; a `--report-dir` flag can select reports for either transcript destination. |
|
||||
| `diagnostics.debug` | `false` | Persist detailed debug artifacts when processing. |
|
||||
| `diagnostics.work_dir` | `/tmp/audita` | Parent of the retained debug run directory; required only when debug is enabled. |
|
||||
|
||||
Module keys select correction modules; validator chains remain built into them.
|
||||
The loader preserves module order and repetitions. Normalization time settings
|
||||
accept numeric seconds only. The operation timeout uses a duration string.
|
||||
`reporting.stdout_dir` must be nonblank only for reporting-enabled stdout runs;
|
||||
`diagnostics.work_dir` must be nonblank only with debug enabled. These path
|
||||
requirements are checked when choosing process destinations, not during
|
||||
configuration inspection.
|
||||
`reporting.stdout_dir` must be nonblank when it supplies the active report
|
||||
destination: for stdout output, or for file output when `--report-dir` is
|
||||
explicitly supplied. File output without that flag uses the transcript's parent,
|
||||
so the configured value is inactive. Reporting-disabled runs do not select a
|
||||
report directory. `diagnostics.work_dir` must be nonblank only with debug
|
||||
enabled. These path requirements are checked when choosing process destinations,
|
||||
not during configuration inspection.
|
||||
|
||||
## Credentials
|
||||
|
||||
|
||||
@@ -9,11 +9,14 @@ The [CLI reference](../cli.md) owns command syntax, streams, and exit semantics;
|
||||
Use an argument array with the transcript path and required glossary argument,
|
||||
following the [process examples](../cli.md#commands-and-help). For machine workflows,
|
||||
select an explicit transcript file destination and a configuration path. Reports
|
||||
then go beside the transcript. For stdout output, select `--report-dir` or use
|
||||
the invocation working directory. Debug is independent and off by default.
|
||||
Supplying a configuration path also
|
||||
makes file selection predictable; only explicitly referenced credential variables
|
||||
are read from the environment.
|
||||
go beside the transcript unless an explicit `--report-dir` selects another
|
||||
directory. For stdout output, reports use the effective `reporting.stdout_dir`
|
||||
(defaulting to the invocation working directory), including a `--report-dir`
|
||||
override. A config-file or overlay value for `reporting.stdout_dir` alone does
|
||||
not redirect file-output reports. `--work-dir` selects a debug root, while
|
||||
configuration or `--debug` enables capture; the path flag alone does not.
|
||||
Supplying a configuration path also makes file selection predictable; only
|
||||
explicitly referenced credential variables are read from the environment.
|
||||
|
||||
Resolve the executable deliberately and set the child's working directory so
|
||||
relative input/output paths and reported artifact paths have a known base.
|
||||
@@ -28,7 +31,8 @@ Consume transcript/report data through the
|
||||
[output-file contract](output-files.md), and treat stderr as human-readable detail.
|
||||
The three reports may be absent after early configuration or destination
|
||||
failures. After initialization, failed reports can describe invalid input or
|
||||
partial processing. Exit `0` can include recoverable model errors and applied
|
||||
partial processing and use the same selected report directory as successful
|
||||
runs. Exit `0` can include recoverable model errors and applied
|
||||
corrections; inspect `errors.yml` and `recoveries.yml` for those outcomes. A
|
||||
transcript may have been written before a later required report failure, so
|
||||
the file alone does not prove completion.
|
||||
|
||||
@@ -17,10 +17,14 @@ settings, and the [output contract](integrations/output-files.md) owns schemas.
|
||||
## Required Output Files
|
||||
|
||||
Reporting is enabled by default. For file transcript output, `errors.yml`,
|
||||
`recoveries.yml`, and `report.md` go in the transcript output's parent directory.
|
||||
For stdout transcript output, they go in `reporting.stdout_dir` (default `.`),
|
||||
overridden by `--report-dir`. `--reporting=false` suppresses all three reports
|
||||
and removes their path requirements. It does not suppress the stderr summary or
|
||||
`recoveries.yml`, and `report.md` go in the transcript output's parent directory
|
||||
unless `--report-dir` is explicitly supplied. For stdout transcript output, they
|
||||
go in the effective `reporting.stdout_dir` (default `.`), including a
|
||||
`--report-dir` override. A config-file or overlay value for
|
||||
`reporting.stdout_dir` alone does not redirect file-output reports. An active
|
||||
selected report directory must be nonblank. `--reporting=false` suppresses all
|
||||
three reports and leaves their destination inactive, so it is not probed merely
|
||||
because `--report-dir` was supplied. It does not suppress the stderr summary or
|
||||
fatal errors. Stdout contains only transcript JSON; file output leaves it empty.
|
||||
|
||||
Existing report directories and parents for regular transcript files must be
|
||||
@@ -32,8 +36,9 @@ Preparation never truncates an existing transcript. Device and pipe destinations
|
||||
(such as `/dev/null` and named pipes) receive ordinary writes at publication time;
|
||||
they are not opened during preflight or replaced with regular files. A pipe write
|
||||
can wait for its reader, and stream write errors are fatal. With reporting enabled,
|
||||
reports still use the output path's parent; disable reporting when that placement
|
||||
is unsuitable for a stream destination.
|
||||
reports still use the output path's parent unless an explicit `--report-dir`
|
||||
selects another directory; disable reporting when that placement is unsuitable
|
||||
for a stream destination.
|
||||
|
||||
With reporting enabled, all three reports are initialized as `in_progress` before
|
||||
input processing. Output is encoded fully before publication. Each regular output
|
||||
@@ -48,10 +53,12 @@ finalized where possible. An uncatchable termination can leave `in_progress`.
|
||||
|
||||
## Diagnostics Directory
|
||||
|
||||
`--debug` enables detailed evidence independently of reporting. One directory
|
||||
named `run-<id>` is created under `diagnostics.work_dir` (default `/tmp/audita`)
|
||||
and retained on success or failure. Without debug, no directory or payload
|
||||
files are created. The parent is created when needed only for debug. Failure to
|
||||
`--debug` or `diagnostics.debug` enables detailed evidence independently of
|
||||
reporting. One directory named `run-<id>` is created under
|
||||
`diagnostics.work_dir` (default `/tmp/audita`), which `--work-dir` can override,
|
||||
and retained on success or failure. Selecting `--work-dir` alone does not enable
|
||||
capture. Without debug, no directory or payload files are created. The parent
|
||||
is created when needed only for debug. Failure to
|
||||
write optional debug evidence prints a redacted stderr diagnostic but does not
|
||||
change correction decisions or the exit status.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Implementation Plan: Independent Output Destinations
|
||||
|
||||
**Last updated:** 2026-10-03
|
||||
**Status:** Stage 1 complete; Stage 2 pending.
|
||||
**Status:** Complete; Stages 1–2 implemented and verified.
|
||||
**Audience:** gpt-5.6-terra coding agent.
|
||||
|
||||
## Scope and Execution
|
||||
@@ -94,6 +94,9 @@ destination constraints, then commit the coherent code/test unit.
|
||||
**Files:** `cmd/audita/main_integration_test.go`, `docs/cli.md`,
|
||||
`docs/config.md`, `docs/operations.md`,
|
||||
`docs/integrations/subprocess.md`, and `docs/roadmap/narratio-feature.md`.
|
||||
|
||||
**Completion:** Complete — the full Go test suite, maintained configuration
|
||||
examples, affected Markdown links, and whitespace checks pass.
|
||||
Touch another document only if its current statement actually conflicts with
|
||||
the new behavior.
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
**Last updated:** 2026-10-03
|
||||
|
||||
**Status:** Proposed; not implemented. This roadmap records the intended external
|
||||
behavior and the reasons for it. The [implementation plan](implementation.md)
|
||||
owns the ordered work; the [Narratio request](feature-request-narratio.md)
|
||||
provides the consumer context.
|
||||
**Status:** Implemented and verified. This roadmap records the external behavior
|
||||
and the reasons for it. The [implementation plan](implementation.md) records the
|
||||
completed work; the [Narratio request](feature-request-narratio.md) provides the
|
||||
consumer context.
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -21,8 +21,9 @@ working-state lifecycle; Narratio owns its destination paths and operator views.
|
||||
- `--output FILE` writes the transcript to `FILE`, or stdout when omitted.
|
||||
- Ordinary reporting is enabled by default. With file output, `errors.yml`,
|
||||
`recoveries.yml`, and `report.md` go beside the transcript. With stdout output,
|
||||
`--report-dir DIR` or `reporting.stdout_dir` selects their directory. The
|
||||
current selection logic ignores `--report-dir` when `--output` is supplied.
|
||||
`--report-dir DIR` or `reporting.stdout_dir` selects their directory. An
|
||||
explicit `--report-dir` also selects reports for file output; without it, the
|
||||
configured `reporting.stdout_dir` value remains stdout-only.
|
||||
- Reports are initialized before input processing, then updated on completion or
|
||||
failure where possible. Early configuration or destination failures can occur
|
||||
before report initialization and therefore leave no report files.
|
||||
@@ -30,10 +31,10 @@ working-state lifecycle; Narratio owns its destination paths and operator views.
|
||||
enable debug capture. Capture is enabled by `diagnostics.debug` or an explicit
|
||||
`--debug`; when enabled, Audita retains a `run-<id>` directory beneath the
|
||||
selected root. Debug remains optional, including after unsuccessful runs.
|
||||
- The output session already receives distinct transcript and report paths and
|
||||
checks active destinations for conflicts with each other and protected inputs.
|
||||
The limitation is primarily in CLI report-directory selection, not in report
|
||||
encoding or the output session's basic shape.
|
||||
- The output session receives distinct transcript and report paths and checks
|
||||
active destinations for conflicts with each other and protected inputs. The
|
||||
CLI selects the report destination while the output session retains its
|
||||
existing collision checks, preflight, initialization, and finalization.
|
||||
|
||||
The current contracts are in [CLI](../cli.md), [configuration](../config.md),
|
||||
[operations](../operations.md), [subprocess guidance](../integrations/subprocess.md),
|
||||
|
||||
@@ -215,6 +215,17 @@ func TestReportingAndDebugOverridesRemainIndependent(t *testing.T) {
|
||||
t.Fatalf("%s did not honor explicit false: %+v", command, selected.Result.Config)
|
||||
}
|
||||
}
|
||||
processInput, _, err := parseConfigInput("process", []string{"input.json", "--glossary", "glossary.yml", "--config", main, "--overlay", "testing"}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
processConfig, err := loadConfigInput(processInput, "process")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, err := selectedReportDir(processConfig.Result.Config, filepath.Join("out", "corrected.json"), processInput.ReportDirSet); err != nil || got != "out" {
|
||||
t.Fatalf("overlay stdout directory redirected file output: %q %v", got, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportDirectorySelectionOnlyUsesActiveDestination(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user