Exclude external paths from analysis fingerprints
This commit is contained in:
@@ -67,11 +67,10 @@ Supported source families:
|
|||||||
JSON payload and no map serialization. Configured artifacts are fingerprinted
|
JSON payload and no map serialization. Configured artifacts are fingerprinted
|
||||||
in deterministic dependency order.
|
in deterministic dependency order.
|
||||||
- fingerprints the normalized artifact key, prompt and profile identifiers,
|
- fingerprints the normalized artifact key, prompt and profile identifiers,
|
||||||
normalized Scriptorium executable and config logical identities, effective
|
effective render-debug behavior, session-relative output identity, sorted
|
||||||
render-debug behavior, session-relative output identity, sorted dependency
|
dependency keys, ordered input declarations and semantic identities,
|
||||||
keys, ordered input declarations and semantic identities, validated current
|
validated current dependency-output identities, and sorted effective
|
||||||
dependency-output identities, and sorted effective Scriptorium variables
|
Scriptorium variables (including Narratio's sticky session variable).
|
||||||
(including Narratio's sticky session variable).
|
|
||||||
- provides read-only reconciliation that classifies each configured record as
|
- provides read-only reconciliation that classifies each configured record as
|
||||||
current, stale, missing, failed, legacy, or otherwise non-resumable, and
|
current, stale, missing, failed, legacy, or otherwise non-resumable, and
|
||||||
separately identifies manifest records removed from current configuration.
|
separately identifies manifest records removed from current configuration.
|
||||||
@@ -155,7 +154,7 @@ Supported source families:
|
|||||||
- input-identity resolution is read-only: it does not invoke adapters,
|
- input-identity resolution is read-only: it does not invoke adapters,
|
||||||
materialize outputs, update status, or create run records.
|
materialize outputs, update status, or create run records.
|
||||||
- fingerprints exclude timeouts, retries, timestamps, producer and Narratio run
|
- fingerprints exclude timeouts, retries, timestamps, producer and Narratio run
|
||||||
IDs, absolute executable/config/workspace roots, diagnostic locations, and
|
IDs, executable and config paths, workspace roots, diagnostic locations, and
|
||||||
executable or private transitive configuration contents. A change that is
|
executable or private transitive configuration contents. A change that is
|
||||||
visible only inside Scriptorium—such as a file privately loaded by its config
|
visible only inside Scriptorium—such as a file privately loaded by its config
|
||||||
path—requires an explicit forced regeneration.
|
path—requires an explicit forced regeneration.
|
||||||
|
|||||||
@@ -49,18 +49,12 @@ type analyzeDependencyOutputIdentity struct {
|
|||||||
// as SHA-256 input. It contains slices and fixed-field structs, never maps.
|
// as SHA-256 input. It contains slices and fixed-field structs, never maps.
|
||||||
type analyzeFingerprintPayload struct {
|
type analyzeFingerprintPayload struct {
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Scriptorium analyzeFingerprintScriptorium `json:"scriptorium"`
|
|
||||||
Artifact analyzeFingerprintArtifact `json:"artifact"`
|
Artifact analyzeFingerprintArtifact `json:"artifact"`
|
||||||
Inputs []analyzeFingerprintInput `json:"inputs"`
|
Inputs []analyzeFingerprintInput `json:"inputs"`
|
||||||
DependencyOutputs []analyzeFingerprintDependencyOutput `json:"dependency_outputs"`
|
DependencyOutputs []analyzeFingerprintDependencyOutput `json:"dependency_outputs"`
|
||||||
Variables []analyzeFingerprintVariable `json:"variables"`
|
Variables []analyzeFingerprintVariable `json:"variables"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type analyzeFingerprintScriptorium struct {
|
|
||||||
ExecutableIdentity string `json:"executable_identity"`
|
|
||||||
ConfigIdentity string `json:"config_identity"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type analyzeFingerprintArtifact struct {
|
type analyzeFingerprintArtifact struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
PromptID string `json:"prompt_id"`
|
PromptID string `json:"prompt_id"`
|
||||||
@@ -169,10 +163,6 @@ func computeAnalyzeArtifactFingerprint(
|
|||||||
}
|
}
|
||||||
payload := analyzeFingerprintPayload{
|
payload := analyzeFingerprintPayload{
|
||||||
Version: manifest.AnalyzeFingerprintContractVersion,
|
Version: manifest.AnalyzeFingerprintContractVersion,
|
||||||
Scriptorium: analyzeFingerprintScriptorium{
|
|
||||||
ExecutableIdentity: normalizedAnalyzeExternalPathIdentity(scriptoriumCfg.Binary),
|
|
||||||
ConfigIdentity: normalizedAnalyzeExternalPathIdentity(scriptoriumCfg.ConfigPath),
|
|
||||||
},
|
|
||||||
Artifact: analyzeFingerprintArtifact{
|
Artifact: analyzeFingerprintArtifact{
|
||||||
Key: key, PromptID: artifactCfg.PromptID, ProfileID: artifactCfg.ProfileID,
|
Key: key, PromptID: artifactCfg.PromptID, ProfileID: artifactCfg.ProfileID,
|
||||||
EffectiveRenderDebug: resolveRenderDebugEnabled(scriptoriumCfg.RenderDebug, artifactCfg.RenderDebug),
|
EffectiveRenderDebug: resolveRenderDebugEnabled(scriptoriumCfg.RenderDebug, artifactCfg.RenderDebug),
|
||||||
@@ -260,18 +250,6 @@ func normalizedAnalyzeDependencyKeys(values []string) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizedAnalyzeExternalPathIdentity(value string) string {
|
|
||||||
trimmed := strings.TrimSpace(value)
|
|
||||||
if trimmed == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
cleaned := filepath.Clean(trimmed)
|
|
||||||
if filepath.IsAbs(cleaned) {
|
|
||||||
return filepath.Base(cleaned)
|
|
||||||
}
|
|
||||||
return filepath.ToSlash(cleaned)
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizedAnalyzeOutputIdentity(value string) (string, error) {
|
func normalizedAnalyzeOutputIdentity(value string) (string, error) {
|
||||||
trimmed := strings.TrimSpace(value)
|
trimmed := strings.TrimSpace(value)
|
||||||
if trimmed == "" {
|
if trimmed == "" {
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ func TestAnalyzeFingerprintCanonicalPayloadSensitivity(t *testing.T) {
|
|||||||
mutate func(*analyzeFingerprintPayload)
|
mutate func(*analyzeFingerprintPayload)
|
||||||
}{
|
}{
|
||||||
{name: "version", mutate: func(p *analyzeFingerprintPayload) { p.Version++ }},
|
{name: "version", mutate: func(p *analyzeFingerprintPayload) { p.Version++ }},
|
||||||
{name: "scriptorium executable", mutate: func(p *analyzeFingerprintPayload) { p.Scriptorium.ExecutableIdentity = "other" }},
|
|
||||||
{name: "scriptorium config", mutate: func(p *analyzeFingerprintPayload) { p.Scriptorium.ConfigIdentity = "other.yml" }},
|
|
||||||
{name: "artifact key", mutate: func(p *analyzeFingerprintPayload) { p.Artifact.Key = "other" }},
|
{name: "artifact key", mutate: func(p *analyzeFingerprintPayload) { p.Artifact.Key = "other" }},
|
||||||
{name: "prompt", mutate: func(p *analyzeFingerprintPayload) { p.Artifact.PromptID = "prompt.changed" }},
|
{name: "prompt", mutate: func(p *analyzeFingerprintPayload) { p.Artifact.PromptID = "prompt.changed" }},
|
||||||
{name: "profile", mutate: func(p *analyzeFingerprintPayload) { p.Artifact.ProfileID = "profile.changed" }},
|
{name: "profile", mutate: func(p *analyzeFingerprintPayload) { p.Artifact.ProfileID = "profile.changed" }},
|
||||||
@@ -87,7 +85,8 @@ func TestComputeAnalyzeFingerprintsIsDeterministicAcrossIncidentalDifferences(t
|
|||||||
{name: "producer run", options: fingerprintFixtureOptions{producerRunID: "run-b", binary: "/opt/a/scriptorium", configPath: "/etc/a/scriptorium.yml", topTimeout: "1m", artifactTimeout: "2m", updatedAt: time.Unix(100, 0).UTC()}},
|
{name: "producer run", options: fingerprintFixtureOptions{producerRunID: "run-b", binary: "/opt/a/scriptorium", configPath: "/etc/a/scriptorium.yml", topTimeout: "1m", artifactTimeout: "2m", updatedAt: time.Unix(100, 0).UTC()}},
|
||||||
{name: "timestamp", options: fingerprintFixtureOptions{producerRunID: "run-a", binary: "/opt/a/scriptorium", configPath: "/etc/a/scriptorium.yml", topTimeout: "1m", artifactTimeout: "2m", updatedAt: time.Unix(999, 0).UTC()}},
|
{name: "timestamp", options: fingerprintFixtureOptions{producerRunID: "run-a", binary: "/opt/a/scriptorium", configPath: "/etc/a/scriptorium.yml", topTimeout: "1m", artifactTimeout: "2m", updatedAt: time.Unix(999, 0).UTC()}},
|
||||||
{name: "timeouts", options: fingerprintFixtureOptions{producerRunID: "run-a", binary: "/opt/a/scriptorium", configPath: "/etc/a/scriptorium.yml", topTimeout: "8m", artifactTimeout: "9m", updatedAt: time.Unix(100, 0).UTC()}},
|
{name: "timeouts", options: fingerprintFixtureOptions{producerRunID: "run-a", binary: "/opt/a/scriptorium", configPath: "/etc/a/scriptorium.yml", topTimeout: "8m", artifactTimeout: "9m", updatedAt: time.Unix(100, 0).UTC()}},
|
||||||
{name: "absolute executable and config paths", options: fingerprintFixtureOptions{producerRunID: "run-a", binary: "/srv/b/scriptorium", configPath: "/srv/b/scriptorium.yml", topTimeout: "1m", artifactTimeout: "2m", updatedAt: time.Unix(100, 0).UTC()}},
|
{name: "absolute executable and config paths", options: fingerprintFixtureOptions{producerRunID: "run-a", binary: "/srv/b/scriptorium-v2", configPath: "/srv/b/alternate.yml", topTimeout: "1m", artifactTimeout: "2m", updatedAt: time.Unix(100, 0).UTC()}},
|
||||||
|
{name: "relative executable and config paths", options: fingerprintFixtureOptions{producerRunID: "run-a", binary: "bin/scriptorium-v2", configPath: "config/alternate.yml", topTimeout: "1m", artifactTimeout: "2m", updatedAt: time.Unix(100, 0).UTC()}},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@@ -109,8 +108,6 @@ func TestComputeAnalyzeFingerprintsTracksEffectiveConfiguration(t *testing.T) {
|
|||||||
{name: "render debug", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", renderDebug: true}},
|
{name: "render debug", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", renderDebug: true}},
|
||||||
{name: "output", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", outputPath: "artifacts/changed.md"}},
|
{name: "output", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", outputPath: "artifacts/changed.md"}},
|
||||||
{name: "effective variable", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", label: "changed"}},
|
{name: "effective variable", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", label: "changed"}},
|
||||||
{name: "executable identity", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", binary: "/opt/bin/other"}},
|
|
||||||
{name: "config identity", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", configPath: "/etc/scriptorium/other.yml"}},
|
|
||||||
{name: "dependency bytes", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "other\n"}},
|
{name: "dependency bytes", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "other\n"}},
|
||||||
{name: "optional input appears", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", includePlayers: true}},
|
{name: "optional input appears", options: fingerprintFixtureOptions{producerRunID: "run-a", dependencyBody: "notes\n", includePlayers: true}},
|
||||||
}
|
}
|
||||||
@@ -170,9 +167,6 @@ func analyzeFingerprintPayloadFixture() analyzeFingerprintPayload {
|
|||||||
}
|
}
|
||||||
return analyzeFingerprintPayload{
|
return analyzeFingerprintPayload{
|
||||||
Version: manifest.AnalyzeFingerprintContractVersion,
|
Version: manifest.AnalyzeFingerprintContractVersion,
|
||||||
Scriptorium: analyzeFingerprintScriptorium{
|
|
||||||
ExecutableIdentity: "scriptorium", ConfigIdentity: "config.yml",
|
|
||||||
},
|
|
||||||
Artifact: analyzeFingerprintArtifact{
|
Artifact: analyzeFingerprintArtifact{
|
||||||
Key: "session_recap", PromptID: "dnd.session_recap", ProfileID: "local",
|
Key: "session_recap", PromptID: "dnd.session_recap", ProfileID: "local",
|
||||||
EffectiveRenderDebug: true, OutputIdentity: "artifacts/session_recap.md",
|
EffectiveRenderDebug: true, OutputIdentity: "artifacts/session_recap.md",
|
||||||
|
|||||||
Reference in New Issue
Block a user